Merge "Move DhcpServer to NetworkStack app" am: 94e5b22985
am: d495691bd5
Change-Id: Ib25c0422fda31c5df7743b481d5f50da209c38fa
This commit is contained in:
@@ -825,7 +825,10 @@ aidl_interface {
|
|||||||
local_include_dir: "core/java",
|
local_include_dir: "core/java",
|
||||||
srcs: [
|
srcs: [
|
||||||
"core/java/android/net/INetworkStackConnector.aidl",
|
"core/java/android/net/INetworkStackConnector.aidl",
|
||||||
|
"core/java/android/net/INetworkStackStatusCallback.aidl",
|
||||||
"core/java/android/net/dhcp/DhcpServingParamsParcel.aidl",
|
"core/java/android/net/dhcp/DhcpServingParamsParcel.aidl",
|
||||||
|
"core/java/android/net/dhcp/IDhcpServer.aidl",
|
||||||
|
"core/java/android/net/dhcp/IDhcpServerCallbacks.aidl",
|
||||||
],
|
],
|
||||||
api_dir: "aidl/networkstack",
|
api_dir: "aidl/networkstack",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2485,6 +2485,8 @@ public class ConnectivityManager {
|
|||||||
public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10;
|
public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10;
|
||||||
/** {@hide} */
|
/** {@hide} */
|
||||||
public static final int TETHER_ERROR_PROVISION_FAILED = 11;
|
public static final int TETHER_ERROR_PROVISION_FAILED = 11;
|
||||||
|
/** {@hide} */
|
||||||
|
public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a more detailed error code after a Tethering or Untethering
|
* Get a more detailed error code after a Tethering or Untethering
|
||||||
|
|||||||
@@ -15,7 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package android.net;
|
package android.net;
|
||||||
|
|
||||||
|
import android.net.dhcp.DhcpServingParamsParcel;
|
||||||
|
import android.net.dhcp.IDhcpServerCallbacks;
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
oneway interface INetworkStackConnector {
|
oneway interface INetworkStackConnector {
|
||||||
// TODO: requestDhcpServer(), etc. will go here
|
void makeDhcpServer(in String ifName, in DhcpServingParamsParcel params,
|
||||||
|
in IDhcpServerCallbacks cb);
|
||||||
}
|
}
|
||||||
22
core/java/android/net/INetworkStackStatusCallback.aidl
Normal file
22
core/java/android/net/INetworkStackStatusCallback.aidl
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.net;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
oneway interface INetworkStackStatusCallback {
|
||||||
|
void onStatusAvailable(int statusCode);
|
||||||
|
}
|
||||||
@@ -25,9 +25,12 @@ import android.content.ComponentName;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.ServiceConnection;
|
import android.content.ServiceConnection;
|
||||||
|
import android.net.dhcp.DhcpServingParamsParcel;
|
||||||
|
import android.net.dhcp.IDhcpServerCallbacks;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
@@ -58,6 +61,22 @@ public class NetworkStack {
|
|||||||
|
|
||||||
public NetworkStack() { }
|
public NetworkStack() { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a DHCP server according to the specified parameters.
|
||||||
|
*
|
||||||
|
* <p>The server will be returned asynchronously through the provided callbacks.
|
||||||
|
*/
|
||||||
|
public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params,
|
||||||
|
final IDhcpServerCallbacks cb) {
|
||||||
|
requestConnector(connector -> {
|
||||||
|
try {
|
||||||
|
connector.makeDhcpServer(ifName, params, cb);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private class NetworkStackConnection implements ServiceConnection {
|
private class NetworkStackConnection implements ServiceConnection {
|
||||||
@Override
|
@Override
|
||||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
|
|||||||
33
core/java/android/net/dhcp/DhcpServerCallbacks.java
Normal file
33
core/java/android/net/dhcp/DhcpServerCallbacks.java
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.net.dhcp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience wrapper around IDhcpServerCallbacks.Stub that implements getInterfaceVersion().
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public abstract class DhcpServerCallbacks extends IDhcpServerCallbacks.Stub {
|
||||||
|
// TODO: add @Override here once the API is versioned
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the version of the aidl interface implemented by the callbacks.
|
||||||
|
*/
|
||||||
|
public int getInterfaceVersion() {
|
||||||
|
// TODO: return IDhcpServerCallbacks.VERSION;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
32
core/java/android/net/dhcp/IDhcpServer.aidl
Normal file
32
core/java/android/net/dhcp/IDhcpServer.aidl
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2018, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing perNmissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.net.dhcp;
|
||||||
|
|
||||||
|
import android.net.INetworkStackStatusCallback;
|
||||||
|
import android.net.dhcp.DhcpServingParamsParcel;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
oneway interface IDhcpServer {
|
||||||
|
const int STATUS_UNKNOWN = 0;
|
||||||
|
const int STATUS_SUCCESS = 1;
|
||||||
|
const int STATUS_INVALID_ARGUMENT = 2;
|
||||||
|
const int STATUS_UNKNOWN_ERROR = 3;
|
||||||
|
|
||||||
|
void start(in INetworkStackStatusCallback cb);
|
||||||
|
void updateParams(in DhcpServingParamsParcel params, in INetworkStackStatusCallback cb);
|
||||||
|
void stop(in INetworkStackStatusCallback cb);
|
||||||
|
}
|
||||||
24
core/java/android/net/dhcp/IDhcpServerCallbacks.aidl
Normal file
24
core/java/android/net/dhcp/IDhcpServerCallbacks.aidl
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2018, The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing perNmissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.net.dhcp;
|
||||||
|
|
||||||
|
import android.net.dhcp.IDhcpServer;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
oneway interface IDhcpServerCallbacks {
|
||||||
|
void onDhcpServerCreated(int statusCode, in IDhcpServer server);
|
||||||
|
}
|
||||||
@@ -22,6 +22,10 @@ java_library {
|
|||||||
srcs: [
|
srcs: [
|
||||||
"src/**/*.java",
|
"src/**/*.java",
|
||||||
],
|
],
|
||||||
|
static_libs: [
|
||||||
|
"dhcp-packet-lib",
|
||||||
|
"frameworks-net-shared-utils",
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updatable network stack packaged as an application
|
// Updatable network stack packaged as an application
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ import static android.net.NetworkUtils.intToInet4AddressHTH;
|
|||||||
import static android.net.NetworkUtils.prefixLengthToV4NetmaskIntHTH;
|
import static android.net.NetworkUtils.prefixLengthToV4NetmaskIntHTH;
|
||||||
import static android.net.dhcp.DhcpLease.EXPIRATION_NEVER;
|
import static android.net.dhcp.DhcpLease.EXPIRATION_NEVER;
|
||||||
import static android.net.dhcp.DhcpLease.inet4AddrToString;
|
import static android.net.dhcp.DhcpLease.inet4AddrToString;
|
||||||
import static android.net.util.NetworkConstants.IPV4_ADDR_BITS;
|
|
||||||
|
import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_BITS;
|
||||||
|
|
||||||
import static java.lang.Math.min;
|
import static java.lang.Math.min;
|
||||||
|
|
||||||
@@ -23,7 +23,8 @@ import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
|
|||||||
import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
|
import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
|
||||||
import static android.net.dhcp.DhcpPacket.DHCP_SERVER;
|
import static android.net.dhcp.DhcpPacket.DHCP_SERVER;
|
||||||
import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
|
import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
|
||||||
import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
|
import static android.net.dhcp.IDhcpServer.STATUS_INVALID_ARGUMENT;
|
||||||
|
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
|
||||||
import static android.system.OsConstants.AF_INET;
|
import static android.system.OsConstants.AF_INET;
|
||||||
import static android.system.OsConstants.IPPROTO_UDP;
|
import static android.system.OsConstants.IPPROTO_UDP;
|
||||||
import static android.system.OsConstants.SOCK_DGRAM;
|
import static android.system.OsConstants.SOCK_DGRAM;
|
||||||
@@ -32,21 +33,28 @@ import static android.system.OsConstants.SO_BINDTODEVICE;
|
|||||||
import static android.system.OsConstants.SO_BROADCAST;
|
import static android.system.OsConstants.SO_BROADCAST;
|
||||||
import static android.system.OsConstants.SO_REUSEADDR;
|
import static android.system.OsConstants.SO_REUSEADDR;
|
||||||
|
|
||||||
|
import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE;
|
||||||
|
import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
|
||||||
|
|
||||||
import static java.lang.Integer.toUnsignedLong;
|
import static java.lang.Integer.toUnsignedLong;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.net.INetworkStackStatusCallback;
|
||||||
import android.net.MacAddress;
|
import android.net.MacAddress;
|
||||||
import android.net.NetworkUtils;
|
import android.net.NetworkUtils;
|
||||||
import android.net.TrafficStats;
|
import android.net.TrafficStats;
|
||||||
import android.net.util.SharedLog;
|
import android.net.util.SharedLog;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.HandlerThread;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.os.RemoteException;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.system.ErrnoException;
|
import android.system.ErrnoException;
|
||||||
import android.system.Os;
|
import android.system.Os;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.HexDump;
|
import com.android.internal.util.HexDump;
|
||||||
@@ -70,7 +78,7 @@ import java.util.ArrayList;
|
|||||||
* on the looper asynchronously.
|
* on the looper asynchronously.
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public class DhcpServer {
|
public class DhcpServer extends IDhcpServer.Stub {
|
||||||
private static final String REPO_TAG = "Repository";
|
private static final String REPO_TAG = "Repository";
|
||||||
|
|
||||||
// Lease time to transmit to client instead of a negative time in case a lease expired before
|
// Lease time to transmit to client instead of a negative time in case a lease expired before
|
||||||
@@ -82,7 +90,7 @@ public class DhcpServer {
|
|||||||
private static final int CMD_UPDATE_PARAMS = 3;
|
private static final int CMD_UPDATE_PARAMS = 3;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private final ServerHandler mHandler;
|
private final HandlerThread mHandlerThread;
|
||||||
@NonNull
|
@NonNull
|
||||||
private final String mIfName;
|
private final String mIfName;
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -93,9 +101,13 @@ public class DhcpServer {
|
|||||||
private final Dependencies mDeps;
|
private final Dependencies mDeps;
|
||||||
@NonNull
|
@NonNull
|
||||||
private final Clock mClock;
|
private final Clock mClock;
|
||||||
@NonNull
|
|
||||||
private final DhcpPacketListener mPacketListener;
|
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private volatile ServerHandler mHandler;
|
||||||
|
|
||||||
|
// Accessed only on the handler thread
|
||||||
|
@Nullable
|
||||||
|
private DhcpPacketListener mPacketListener;
|
||||||
@Nullable
|
@Nullable
|
||||||
private FileDescriptor mSocket;
|
private FileDescriptor mSocket;
|
||||||
@NonNull
|
@NonNull
|
||||||
@@ -156,6 +168,12 @@ public class DhcpServer {
|
|||||||
*/
|
*/
|
||||||
void addArpEntry(@NonNull Inet4Address ipv4Addr, @NonNull MacAddress ethAddr,
|
void addArpEntry(@NonNull Inet4Address ipv4Addr, @NonNull MacAddress ethAddr,
|
||||||
@NonNull String ifname, @NonNull FileDescriptor fd) throws IOException;
|
@NonNull String ifname, @NonNull FileDescriptor fd) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify that the caller is allowed to call public methods on DhcpServer.
|
||||||
|
* @throws SecurityException The caller is not allowed to call public methods on DhcpServer.
|
||||||
|
*/
|
||||||
|
void checkCaller() throws SecurityException;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DependenciesImpl implements Dependencies {
|
private class DependenciesImpl implements Dependencies {
|
||||||
@@ -189,6 +207,11 @@ public class DhcpServer {
|
|||||||
@NonNull String ifname, @NonNull FileDescriptor fd) throws IOException {
|
@NonNull String ifname, @NonNull FileDescriptor fd) throws IOException {
|
||||||
NetworkUtils.addArpEntry(ipv4Addr, ethAddr, ifname, fd);
|
NetworkUtils.addArpEntry(ipv4Addr, ethAddr, ifname, fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void checkCaller() {
|
||||||
|
checkNetworkStackCallingPermission();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class MalformedPacketException extends Exception {
|
private static class MalformedPacketException extends Exception {
|
||||||
@@ -197,41 +220,62 @@ public class DhcpServer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DhcpServer(@NonNull Looper looper, @NonNull String ifName,
|
public DhcpServer(@NonNull String ifName,
|
||||||
@NonNull DhcpServingParams params, @NonNull SharedLog log) {
|
@NonNull DhcpServingParams params, @NonNull SharedLog log) {
|
||||||
this(looper, ifName, params, log, null);
|
this(new HandlerThread(DhcpServer.class.getSimpleName() + "." + ifName),
|
||||||
|
ifName, params, log, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
DhcpServer(@NonNull Looper looper, @NonNull String ifName,
|
DhcpServer(@NonNull HandlerThread handlerThread, @NonNull String ifName,
|
||||||
@NonNull DhcpServingParams params, @NonNull SharedLog log,
|
@NonNull DhcpServingParams params, @NonNull SharedLog log,
|
||||||
@Nullable Dependencies deps) {
|
@Nullable Dependencies deps) {
|
||||||
if (deps == null) {
|
if (deps == null) {
|
||||||
deps = new DependenciesImpl();
|
deps = new DependenciesImpl();
|
||||||
}
|
}
|
||||||
mHandler = new ServerHandler(looper);
|
mHandlerThread = handlerThread;
|
||||||
mIfName = ifName;
|
mIfName = ifName;
|
||||||
mServingParams = params;
|
mServingParams = params;
|
||||||
mLog = log;
|
mLog = log;
|
||||||
mDeps = deps;
|
mDeps = deps;
|
||||||
mClock = deps.makeClock();
|
mClock = deps.makeClock();
|
||||||
mPacketListener = deps.makePacketListener();
|
|
||||||
mLeaseRepo = deps.makeLeaseRepository(mServingParams, mLog, mClock);
|
mLeaseRepo = deps.makeLeaseRepository(mServingParams, mLog, mClock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start listening for and responding to packets.
|
* Start listening for and responding to packets.
|
||||||
|
*
|
||||||
|
* <p>It is not legal to call this method more than once; in particular the server cannot be
|
||||||
|
* restarted after being stopped.
|
||||||
*/
|
*/
|
||||||
public void start() {
|
@Override
|
||||||
mHandler.sendEmptyMessage(CMD_START_DHCP_SERVER);
|
public void start(@Nullable INetworkStackStatusCallback cb) {
|
||||||
|
mDeps.checkCaller();
|
||||||
|
mHandlerThread.start();
|
||||||
|
mHandler = new ServerHandler(mHandlerThread.getLooper());
|
||||||
|
sendMessage(CMD_START_DHCP_SERVER, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update serving parameters. All subsequently received requests will be handled with the new
|
* Update serving parameters. All subsequently received requests will be handled with the new
|
||||||
* parameters, and current leases that are incompatible with the new parameters are dropped.
|
* parameters, and current leases that are incompatible with the new parameters are dropped.
|
||||||
*/
|
*/
|
||||||
public void updateParams(@NonNull DhcpServingParams params) {
|
@Override
|
||||||
sendMessage(CMD_UPDATE_PARAMS, params);
|
public void updateParams(@Nullable DhcpServingParamsParcel params,
|
||||||
|
@Nullable INetworkStackStatusCallback cb) throws RemoteException {
|
||||||
|
mDeps.checkCaller();
|
||||||
|
final DhcpServingParams parsedParams;
|
||||||
|
try {
|
||||||
|
// throws InvalidParameterException with null params
|
||||||
|
parsedParams = DhcpServingParams.fromParcelableObject(params);
|
||||||
|
} catch (DhcpServingParams.InvalidParameterException e) {
|
||||||
|
mLog.e("Invalid parameters sent to DhcpServer", e);
|
||||||
|
if (cb != null) {
|
||||||
|
cb.onStatusAvailable(STATUS_INVALID_ARGUMENT);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sendMessage(CMD_UPDATE_PARAMS, new Pair<>(parsedParams, cb));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -240,11 +284,17 @@ public class DhcpServer {
|
|||||||
* <p>As the server is stopped asynchronously, some packets may still be processed shortly after
|
* <p>As the server is stopped asynchronously, some packets may still be processed shortly after
|
||||||
* calling this method.
|
* calling this method.
|
||||||
*/
|
*/
|
||||||
public void stop() {
|
@Override
|
||||||
mHandler.sendEmptyMessage(CMD_STOP_DHCP_SERVER);
|
public void stop(@Nullable INetworkStackStatusCallback cb) {
|
||||||
|
mDeps.checkCaller();
|
||||||
|
sendMessage(CMD_STOP_DHCP_SERVER, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendMessage(int what, @Nullable Object obj) {
|
private void sendMessage(int what, @Nullable Object obj) {
|
||||||
|
if (mHandler == null) {
|
||||||
|
mLog.e("Attempting to send a command to stopped DhcpServer: " + what);
|
||||||
|
return;
|
||||||
|
}
|
||||||
mHandler.sendMessage(mHandler.obtainMessage(what, obj));
|
mHandler.sendMessage(mHandler.obtainMessage(what, obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,23 +305,42 @@ public class DhcpServer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleMessage(@NonNull Message msg) {
|
public void handleMessage(@NonNull Message msg) {
|
||||||
|
final INetworkStackStatusCallback cb;
|
||||||
switch (msg.what) {
|
switch (msg.what) {
|
||||||
case CMD_UPDATE_PARAMS:
|
case CMD_UPDATE_PARAMS:
|
||||||
final DhcpServingParams params = (DhcpServingParams) msg.obj;
|
final Pair<DhcpServingParams, INetworkStackStatusCallback> pair =
|
||||||
|
(Pair<DhcpServingParams, INetworkStackStatusCallback>) msg.obj;
|
||||||
|
final DhcpServingParams params = pair.first;
|
||||||
mServingParams = params;
|
mServingParams = params;
|
||||||
mLeaseRepo.updateParams(
|
mLeaseRepo.updateParams(
|
||||||
DhcpServingParams.makeIpPrefix(mServingParams.serverAddr),
|
DhcpServingParams.makeIpPrefix(mServingParams.serverAddr),
|
||||||
params.excludedAddrs,
|
params.excludedAddrs,
|
||||||
params.dhcpLeaseTimeSecs);
|
params.dhcpLeaseTimeSecs);
|
||||||
|
|
||||||
|
cb = pair.second;
|
||||||
break;
|
break;
|
||||||
case CMD_START_DHCP_SERVER:
|
case CMD_START_DHCP_SERVER:
|
||||||
// This is a no-op if the listener is already started
|
mPacketListener = mDeps.makePacketListener();
|
||||||
mPacketListener.start();
|
mPacketListener.start();
|
||||||
|
cb = (INetworkStackStatusCallback) msg.obj;
|
||||||
break;
|
break;
|
||||||
case CMD_STOP_DHCP_SERVER:
|
case CMD_STOP_DHCP_SERVER:
|
||||||
// This is a no-op if the listener was not started
|
if (mPacketListener != null) {
|
||||||
mPacketListener.stop();
|
mPacketListener.stop();
|
||||||
|
mPacketListener = null;
|
||||||
|
}
|
||||||
|
mHandlerThread.quitSafely();
|
||||||
|
cb = (INetworkStackStatusCallback) msg.obj;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cb != null) {
|
||||||
|
try {
|
||||||
|
cb.onStatusAvailable(STATUS_SUCCESS);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
mLog.e("Could not send status back to caller", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -572,7 +641,7 @@ public class DhcpServer {
|
|||||||
return mSocket;
|
return mSocket;
|
||||||
} catch (IOException | ErrnoException e) {
|
} catch (IOException | ErrnoException e) {
|
||||||
mLog.e("Error creating UDP socket", e);
|
mLog.e("Error creating UDP socket", e);
|
||||||
DhcpServer.this.stop();
|
DhcpServer.this.stop(null);
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
TrafficStats.setThreadStatsTag(oldTag);
|
TrafficStats.setThreadStatsTag(oldTag);
|
||||||
@@ -18,9 +18,10 @@ package android.net.dhcp;
|
|||||||
|
|
||||||
import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
|
import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
|
||||||
import static android.net.NetworkUtils.intToInet4AddressHTH;
|
import static android.net.NetworkUtils.intToInet4AddressHTH;
|
||||||
import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
|
|
||||||
import static android.net.util.NetworkConstants.IPV4_MAX_MTU;
|
import static com.android.server.util.NetworkStackConstants.INFINITE_LEASE;
|
||||||
import static android.net.util.NetworkConstants.IPV4_MIN_MTU;
|
import static com.android.server.util.NetworkStackConstants.IPV4_MAX_MTU;
|
||||||
|
import static com.android.server.util.NetworkStackConstants.IPV4_MIN_MTU;
|
||||||
|
|
||||||
import static java.lang.Integer.toUnsignedLong;
|
import static java.lang.Integer.toUnsignedLong;
|
||||||
|
|
||||||
@@ -107,9 +108,13 @@ public class DhcpServingParams {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Create parameters from a stable AIDL-compatible parcel.
|
* Create parameters from a stable AIDL-compatible parcel.
|
||||||
|
* @throws InvalidParameterException The parameters parcelable is null or invalid.
|
||||||
*/
|
*/
|
||||||
public static DhcpServingParams fromParcelableObject(@NonNull DhcpServingParamsParcel parcel)
|
public static DhcpServingParams fromParcelableObject(@Nullable DhcpServingParamsParcel parcel)
|
||||||
throws InvalidParameterException {
|
throws InvalidParameterException {
|
||||||
|
if (parcel == null) {
|
||||||
|
throw new InvalidParameterException("Null serving parameters");
|
||||||
|
}
|
||||||
final LinkAddress serverAddr = new LinkAddress(
|
final LinkAddress serverAddr = new LinkAddress(
|
||||||
intToInet4AddressHTH(parcel.serverAddr),
|
intToInet4AddressHTH(parcel.serverAddr),
|
||||||
parcel.serverAddrPrefixLength);
|
parcel.serverAddrPrefixLength);
|
||||||
197
packages/NetworkStack/src/android/net/util/SharedLog.java
Normal file
197
packages/NetworkStack/src/android/net/util/SharedLog.java
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package android.net.util;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.LocalLog;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.io.FileDescriptor;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.StringJoiner;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to centralize logging functionality for tethering.
|
||||||
|
*
|
||||||
|
* All access to class methods other than dump() must be on the same thread.
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public class SharedLog {
|
||||||
|
private static final int DEFAULT_MAX_RECORDS = 500;
|
||||||
|
private static final String COMPONENT_DELIMITER = ".";
|
||||||
|
|
||||||
|
private enum Category {
|
||||||
|
NONE,
|
||||||
|
ERROR,
|
||||||
|
MARK,
|
||||||
|
WARN,
|
||||||
|
};
|
||||||
|
|
||||||
|
private final LocalLog mLocalLog;
|
||||||
|
// The tag to use for output to the system log. This is not output to the
|
||||||
|
// LocalLog because that would be redundant.
|
||||||
|
private final String mTag;
|
||||||
|
// The component (or subcomponent) of a system that is sharing this log.
|
||||||
|
// This can grow in depth if components call forSubComponent() to obtain
|
||||||
|
// their SharedLog instance. The tag is not included in the component for
|
||||||
|
// brevity.
|
||||||
|
private final String mComponent;
|
||||||
|
|
||||||
|
public SharedLog(String tag) {
|
||||||
|
this(DEFAULT_MAX_RECORDS, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SharedLog(int maxRecords, String tag) {
|
||||||
|
this(new LocalLog(maxRecords), tag, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SharedLog(LocalLog localLog, String tag, String component) {
|
||||||
|
mLocalLog = localLog;
|
||||||
|
mTag = tag;
|
||||||
|
mComponent = component;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a SharedLog based on this log with an additional component prefix on each logged line.
|
||||||
|
*/
|
||||||
|
public SharedLog forSubComponent(String component) {
|
||||||
|
if (!isRootLogInstance()) {
|
||||||
|
component = mComponent + COMPONENT_DELIMITER + component;
|
||||||
|
}
|
||||||
|
return new SharedLog(mLocalLog, mTag, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump the contents of this log.
|
||||||
|
*
|
||||||
|
* <p>This method may be called on any thread.
|
||||||
|
*/
|
||||||
|
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
|
||||||
|
mLocalLog.readOnlyLocalLog().dump(fd, writer, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////
|
||||||
|
// Methods that both log an entry and emit it to the system log.
|
||||||
|
//////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log an error due to an exception. This does not include the exception stacktrace.
|
||||||
|
*
|
||||||
|
* <p>The log entry will be also added to the system log.
|
||||||
|
* @see #e(String, Throwable)
|
||||||
|
*/
|
||||||
|
public void e(Exception e) {
|
||||||
|
Log.e(mTag, record(Category.ERROR, e.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log an error message.
|
||||||
|
*
|
||||||
|
* <p>The log entry will be also added to the system log.
|
||||||
|
*/
|
||||||
|
public void e(String msg) {
|
||||||
|
Log.e(mTag, record(Category.ERROR, msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log an error due to an exception, with the exception stacktrace if provided.
|
||||||
|
*
|
||||||
|
* <p>The error and exception message appear in the shared log, but the stacktrace is only
|
||||||
|
* logged in general log output (logcat). The log entry will be also added to the system log.
|
||||||
|
*/
|
||||||
|
public void e(@NonNull String msg, @Nullable Throwable exception) {
|
||||||
|
if (exception == null) {
|
||||||
|
e(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.e(mTag, record(Category.ERROR, msg + ": " + exception.getMessage()), exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log an informational message.
|
||||||
|
*
|
||||||
|
* <p>The log entry will be also added to the system log.
|
||||||
|
*/
|
||||||
|
public void i(String msg) {
|
||||||
|
Log.i(mTag, record(Category.NONE, msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a warning message.
|
||||||
|
*
|
||||||
|
* <p>The log entry will be also added to the system log.
|
||||||
|
*/
|
||||||
|
public void w(String msg) {
|
||||||
|
Log.w(mTag, record(Category.WARN, msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
//////
|
||||||
|
// Methods that only log an entry (and do NOT emit to the system log).
|
||||||
|
//////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a general message to be only included in the in-memory log.
|
||||||
|
*
|
||||||
|
* <p>The log entry will *not* be added to the system log.
|
||||||
|
*/
|
||||||
|
public void log(String msg) {
|
||||||
|
record(Category.NONE, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a general, formatted message to be only included in the in-memory log.
|
||||||
|
*
|
||||||
|
* <p>The log entry will *not* be added to the system log.
|
||||||
|
* @see String#format(String, Object...)
|
||||||
|
*/
|
||||||
|
public void logf(String fmt, Object... args) {
|
||||||
|
log(String.format(fmt, args));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a message with MARK level.
|
||||||
|
*
|
||||||
|
* <p>The log entry will *not* be added to the system log.
|
||||||
|
*/
|
||||||
|
public void mark(String msg) {
|
||||||
|
record(Category.MARK, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String record(Category category, String msg) {
|
||||||
|
final String entry = logLine(category, msg);
|
||||||
|
mLocalLog.log(entry);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String logLine(Category category, String msg) {
|
||||||
|
final StringJoiner sj = new StringJoiner(" ");
|
||||||
|
if (!isRootLogInstance()) sj.add("[" + mComponent + "]");
|
||||||
|
if (category != Category.NONE) sj.add(category.toString());
|
||||||
|
return sj.add(msg).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether this SharedLog instance is nominally the top level in
|
||||||
|
// a potential hierarchy of shared logs (the root of a tree),
|
||||||
|
// or is a subcomponent within the hierarchy.
|
||||||
|
private boolean isRootLogInstance() {
|
||||||
|
return TextUtils.isEmpty(mComponent) || mComponent.equals(mTag);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,15 +16,24 @@
|
|||||||
|
|
||||||
package com.android.server;
|
package com.android.server;
|
||||||
|
|
||||||
import static android.os.Binder.getCallingUid;
|
import static android.net.dhcp.IDhcpServer.STATUS_INVALID_ARGUMENT;
|
||||||
|
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
|
||||||
|
import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR;
|
||||||
|
|
||||||
|
import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.INetworkStackConnector;
|
import android.net.INetworkStackConnector;
|
||||||
|
import android.net.dhcp.DhcpServer;
|
||||||
|
import android.net.dhcp.DhcpServingParams;
|
||||||
|
import android.net.dhcp.DhcpServingParamsParcel;
|
||||||
|
import android.net.dhcp.IDhcpServerCallbacks;
|
||||||
|
import android.net.util.SharedLog;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Process;
|
import android.os.RemoteException;
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
@@ -54,21 +63,37 @@ public class NetworkStackService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class NetworkStackConnector extends INetworkStackConnector.Stub {
|
private static class NetworkStackConnector extends INetworkStackConnector.Stub {
|
||||||
// TODO: makeDhcpServer(), etc. will go here.
|
@NonNull
|
||||||
|
private final SharedLog mLog = new SharedLog(TAG);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void makeDhcpServer(@NonNull String ifName, @NonNull DhcpServingParamsParcel params,
|
||||||
|
@NonNull IDhcpServerCallbacks cb) throws RemoteException {
|
||||||
|
checkNetworkStackCallingPermission();
|
||||||
|
final DhcpServer server;
|
||||||
|
try {
|
||||||
|
server = new DhcpServer(
|
||||||
|
ifName,
|
||||||
|
DhcpServingParams.fromParcelableObject(params),
|
||||||
|
mLog.forSubComponent(ifName + ".DHCP"));
|
||||||
|
} catch (DhcpServingParams.InvalidParameterException e) {
|
||||||
|
mLog.e("Invalid DhcpServingParams", e);
|
||||||
|
cb.onDhcpServerCreated(STATUS_INVALID_ARGUMENT, null);
|
||||||
|
return;
|
||||||
|
} catch (Exception e) {
|
||||||
|
mLog.e("Unknown error starting DhcpServer", e);
|
||||||
|
cb.onDhcpServerCreated(STATUS_UNKNOWN_ERROR, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cb.onDhcpServerCreated(STATUS_SUCCESS, server);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
|
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
|
||||||
@Nullable String[] args) {
|
@Nullable String[] args) {
|
||||||
checkCaller();
|
checkNetworkStackCallingPermission();
|
||||||
fout.println("NetworkStack logs:");
|
fout.println("NetworkStack logs:");
|
||||||
// TODO: dump logs here
|
mLog.dump(fd, fout, args);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkCaller() {
|
|
||||||
// TODO: check that the calling PID is the system server.
|
|
||||||
if (getCallingUid() != Process.SYSTEM_UID && getCallingUid() != Process.ROOT_UID) {
|
|
||||||
throw new SecurityException("Invalid caller: " + getCallingUid());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.server.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Network constants used by the network stack.
|
||||||
|
*/
|
||||||
|
public final class NetworkStackConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IPv4 constants.
|
||||||
|
*
|
||||||
|
* See also:
|
||||||
|
* - https://tools.ietf.org/html/rfc791
|
||||||
|
*/
|
||||||
|
public static final int IPV4_ADDR_BITS = 32;
|
||||||
|
public static final int IPV4_MIN_MTU = 68;
|
||||||
|
public static final int IPV4_MAX_MTU = 65_535;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DHCP constants.
|
||||||
|
*
|
||||||
|
* See also:
|
||||||
|
* - https://tools.ietf.org/html/rfc2131
|
||||||
|
*/
|
||||||
|
public static final int INFINITE_LEASE = 0xffffffff;
|
||||||
|
|
||||||
|
private NetworkStackConstants() {
|
||||||
|
throw new UnsupportedOperationException("This class is not to be instantiated");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2018 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.server.util;
|
||||||
|
|
||||||
|
import static android.os.Binder.getCallingUid;
|
||||||
|
|
||||||
|
import android.os.Process;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class to check calling permissions on the network stack.
|
||||||
|
*/
|
||||||
|
public final class PermissionUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check that the caller is allowed to communicate with the network stack.
|
||||||
|
* @throws SecurityException The caller is not allowed to communicate with the network stack.
|
||||||
|
*/
|
||||||
|
public static void checkNetworkStackCallingPermission() {
|
||||||
|
// TODO: check that the calling PID is the system server.
|
||||||
|
if (getCallingUid() != Process.SYSTEM_UID && getCallingUid() != Process.ROOT_UID) {
|
||||||
|
throw new SecurityException("Invalid caller: " + getCallingUid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PermissionUtil() {
|
||||||
|
throw new UnsupportedOperationException("This class is not to be instantiated");
|
||||||
|
}
|
||||||
|
}
|
||||||
35
packages/NetworkStack/tests/Android.bp
Normal file
35
packages/NetworkStack/tests/Android.bp
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
//
|
||||||
|
// Copyright (C) 2018 The Android Open Source Project
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
|
||||||
|
android_test {
|
||||||
|
name: "NetworkStackTests",
|
||||||
|
srcs: ["src/**/*.java"],
|
||||||
|
static_libs: [
|
||||||
|
"android-support-test",
|
||||||
|
"mockito-target-extended-minus-junit4",
|
||||||
|
"NetworkStackLib",
|
||||||
|
"testables",
|
||||||
|
],
|
||||||
|
libs: [
|
||||||
|
"android.test.runner",
|
||||||
|
"android.test.base",
|
||||||
|
],
|
||||||
|
jni_libs: [
|
||||||
|
// For mockito extended
|
||||||
|
"libdexmakerjvmtiagent",
|
||||||
|
"libstaticjvmtiagent",
|
||||||
|
]
|
||||||
|
}
|
||||||
25
packages/NetworkStack/tests/AndroidManifest.xml
Normal file
25
packages/NetworkStack/tests/AndroidManifest.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2018 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.android.server.networkstack.tests">
|
||||||
|
<application android:debuggable="true">
|
||||||
|
<uses-library android:name="android.test.runner" />
|
||||||
|
</application>
|
||||||
|
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
|
||||||
|
android:targetPackage="com.android.server.networkstack.tests"
|
||||||
|
android:label="Networking service tests">
|
||||||
|
</instrumentation>
|
||||||
|
</manifest>
|
||||||
29
packages/NetworkStack/tests/AndroidTest.xml
Normal file
29
packages/NetworkStack/tests/AndroidTest.xml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright (C) 2018 The Android Open Source Project
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
-->
|
||||||
|
<configuration description="Runs Tests for NetworkStack">
|
||||||
|
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
|
||||||
|
<option name="test-file-name" value="NetworkStackTests.apk" />
|
||||||
|
</target_preparer>
|
||||||
|
|
||||||
|
<option name="test-suite-tag" value="apct" />
|
||||||
|
<option name="test-suite-tag" value="framework-base-presubmit" />
|
||||||
|
<option name="test-tag" value="NetworkStackTests" />
|
||||||
|
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
|
||||||
|
<option name="package" value="com.android.server.networkstack.tests" />
|
||||||
|
<option name="runner" value="android.support.test.runner.AndroidJUnitRunner" />
|
||||||
|
<option name="hidden-api-checks" value="false"/>
|
||||||
|
</test>
|
||||||
|
</configuration>
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package android.net.dhcp;
|
package android.net.dhcp;
|
||||||
|
|
||||||
|
import static android.net.InetAddresses.parseNumericAddress;
|
||||||
import static android.net.dhcp.DhcpLease.HOSTNAME_NONE;
|
import static android.net.dhcp.DhcpLease.HOSTNAME_NONE;
|
||||||
import static android.net.dhcp.DhcpLeaseRepository.CLIENTID_UNSPEC;
|
import static android.net.dhcp.DhcpLeaseRepository.CLIENTID_UNSPEC;
|
||||||
import static android.net.dhcp.DhcpLeaseRepository.INETADDR_UNSPEC;
|
import static android.net.dhcp.DhcpLeaseRepository.INETADDR_UNSPEC;
|
||||||
@@ -29,7 +30,6 @@ import static org.junit.Assert.fail;
|
|||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import static java.lang.String.format;
|
import static java.lang.String.format;
|
||||||
import static java.net.InetAddress.parseNumericAddress;
|
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
@@ -16,11 +16,13 @@
|
|||||||
|
|
||||||
package android.net.dhcp;
|
package android.net.dhcp;
|
||||||
|
|
||||||
|
import static android.net.InetAddresses.parseNumericAddress;
|
||||||
import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
|
import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
|
||||||
import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
|
import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
|
||||||
import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
|
import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
|
||||||
import static android.net.dhcp.DhcpPacket.INADDR_ANY;
|
import static android.net.dhcp.DhcpPacket.INADDR_ANY;
|
||||||
import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
|
import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
|
||||||
|
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
|
||||||
|
|
||||||
import static junit.framework.Assert.assertEquals;
|
import static junit.framework.Assert.assertEquals;
|
||||||
import static junit.framework.Assert.assertFalse;
|
import static junit.framework.Assert.assertFalse;
|
||||||
@@ -33,14 +35,14 @@ import static org.mockito.ArgumentMatchers.eq;
|
|||||||
import static org.mockito.ArgumentMatchers.isNull;
|
import static org.mockito.ArgumentMatchers.isNull;
|
||||||
import static org.mockito.Mockito.doNothing;
|
import static org.mockito.Mockito.doNothing;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import static java.net.InetAddress.parseNumericAddress;
|
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
|
import android.net.INetworkStackStatusCallback;
|
||||||
import android.net.LinkAddress;
|
import android.net.LinkAddress;
|
||||||
import android.net.MacAddress;
|
import android.net.MacAddress;
|
||||||
import android.net.dhcp.DhcpLeaseRepository.InvalidAddressException;
|
import android.net.dhcp.DhcpLeaseRepository.InvalidAddressException;
|
||||||
@@ -48,9 +50,11 @@ import android.net.dhcp.DhcpLeaseRepository.OutOfAddressesException;
|
|||||||
import android.net.dhcp.DhcpServer.Clock;
|
import android.net.dhcp.DhcpServer.Clock;
|
||||||
import android.net.dhcp.DhcpServer.Dependencies;
|
import android.net.dhcp.DhcpServer.Dependencies;
|
||||||
import android.net.util.SharedLog;
|
import android.net.util.SharedLog;
|
||||||
import android.os.test.TestLooper;
|
import android.os.HandlerThread;
|
||||||
import android.support.test.filters.SmallTest;
|
import android.support.test.filters.SmallTest;
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
import android.testing.AndroidTestingRunner;
|
||||||
|
import android.testing.TestableLooper;
|
||||||
|
import android.testing.TestableLooper.RunWithLooper;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@@ -67,10 +71,10 @@ import java.util.Arrays;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidTestingRunner.class)
|
||||||
@SmallTest
|
@SmallTest
|
||||||
|
@RunWithLooper
|
||||||
public class DhcpServerTest {
|
public class DhcpServerTest {
|
||||||
private static final String PROP_DEXMAKER_SHARE_CLASSLOADER = "dexmaker.share_classloader";
|
|
||||||
private static final String TEST_IFACE = "testiface";
|
private static final String TEST_IFACE = "testiface";
|
||||||
|
|
||||||
private static final Inet4Address TEST_SERVER_ADDR = parseAddr("192.168.0.2");
|
private static final Inet4Address TEST_SERVER_ADDR = parseAddr("192.168.0.2");
|
||||||
@@ -113,18 +117,25 @@ public class DhcpServerTest {
|
|||||||
private ArgumentCaptor<Inet4Address> mResponseDstAddrCaptor;
|
private ArgumentCaptor<Inet4Address> mResponseDstAddrCaptor;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private TestLooper mLooper;
|
private HandlerThread mHandlerThread;
|
||||||
|
@NonNull
|
||||||
|
private TestableLooper mLooper;
|
||||||
@NonNull
|
@NonNull
|
||||||
private DhcpServer mServer;
|
private DhcpServer mServer;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private String mPrevShareClassloaderProp;
|
private String mPrevShareClassloaderProp;
|
||||||
|
|
||||||
|
private final INetworkStackStatusCallback mAssertSuccessCallback =
|
||||||
|
new INetworkStackStatusCallback.Stub() {
|
||||||
|
@Override
|
||||||
|
public void onStatusAvailable(int statusCode) {
|
||||||
|
assertEquals(STATUS_SUCCESS, statusCode);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
// Allow mocking package-private classes
|
|
||||||
mPrevShareClassloaderProp = System.getProperty(PROP_DEXMAKER_SHARE_CLASSLOADER);
|
|
||||||
System.setProperty(PROP_DEXMAKER_SHARE_CLASSLOADER, "true");
|
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
when(mDeps.makeLeaseRepository(any(), any(), any())).thenReturn(mRepository);
|
when(mDeps.makeLeaseRepository(any(), any(), any())).thenReturn(mRepository);
|
||||||
@@ -143,20 +154,22 @@ public class DhcpServerTest {
|
|||||||
.setExcludedAddrs(TEST_EXCLUDED_ADDRS)
|
.setExcludedAddrs(TEST_EXCLUDED_ADDRS)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
mLooper = new TestLooper();
|
mLooper = TestableLooper.get(this);
|
||||||
mServer = new DhcpServer(mLooper.getLooper(), TEST_IFACE, servingParams,
|
mHandlerThread = spy(new HandlerThread("TestDhcpServer"));
|
||||||
|
when(mHandlerThread.getLooper()).thenReturn(mLooper.getLooper());
|
||||||
|
mServer = new DhcpServer(mHandlerThread, TEST_IFACE, servingParams,
|
||||||
new SharedLog(DhcpServerTest.class.getSimpleName()), mDeps);
|
new SharedLog(DhcpServerTest.class.getSimpleName()), mDeps);
|
||||||
|
|
||||||
mServer.start();
|
mServer.start(mAssertSuccessCallback);
|
||||||
mLooper.dispatchAll();
|
mLooper.processAllMessages();
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() throws Exception {
|
||||||
// Calling stop() several times is not an issue
|
mServer.stop(mAssertSuccessCallback);
|
||||||
mServer.stop();
|
mLooper.processMessages(1);
|
||||||
System.setProperty(PROP_DEXMAKER_SHARE_CLASSLOADER,
|
verify(mPacketListener, times(1)).stop();
|
||||||
(mPrevShareClassloaderProp == null ? "" : mPrevShareClassloaderProp));
|
verify(mHandlerThread, times(1)).quitSafely();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -164,13 +177,6 @@ public class DhcpServerTest {
|
|||||||
verify(mPacketListener, times(1)).start();
|
verify(mPacketListener, times(1)).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testStop() throws Exception {
|
|
||||||
mServer.stop();
|
|
||||||
mLooper.dispatchAll();
|
|
||||||
verify(mPacketListener, times(1)).stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDiscover() throws Exception {
|
public void testDiscover() throws Exception {
|
||||||
// TODO: refactor packet construction to eliminate unnecessary/confusing/duplicate fields
|
// TODO: refactor packet construction to eliminate unnecessary/confusing/duplicate fields
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package android.net.dhcp;
|
package android.net.dhcp;
|
||||||
|
|
||||||
|
import static android.net.InetAddresses.parseNumericAddress;
|
||||||
import static android.net.NetworkUtils.inet4AddressToIntHTH;
|
import static android.net.NetworkUtils.inet4AddressToIntHTH;
|
||||||
import static android.net.dhcp.DhcpServingParams.MTU_UNSET;
|
import static android.net.dhcp.DhcpServingParams.MTU_UNSET;
|
||||||
|
|
||||||
@@ -23,8 +24,6 @@ import static junit.framework.Assert.assertEquals;
|
|||||||
import static junit.framework.Assert.assertFalse;
|
import static junit.framework.Assert.assertFalse;
|
||||||
import static junit.framework.Assert.assertTrue;
|
import static junit.framework.Assert.assertTrue;
|
||||||
|
|
||||||
import static java.net.InetAddress.parseNumericAddress;
|
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.net.LinkAddress;
|
import android.net.LinkAddress;
|
||||||
@@ -195,6 +194,11 @@ public class DhcpServingParamsTest {
|
|||||||
assertEquals(7, numFields);
|
assertEquals(7, numFields);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = InvalidParameterException.class)
|
||||||
|
public void testFromParcelableObject_NullArgument() throws InvalidParameterException {
|
||||||
|
DhcpServingParams.fromParcelableObject(null);
|
||||||
|
}
|
||||||
|
|
||||||
private static int[] toIntArray(Collection<Inet4Address> addrs) {
|
private static int[] toIntArray(Collection<Inet4Address> addrs) {
|
||||||
return addrs.stream().mapToInt(NetworkUtils::inet4AddressToIntHTH).toArray();
|
return addrs.stream().mapToInt(NetworkUtils::inet4AddressToIntHTH).toArray();
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import android.net.util.SharedLog;
|
||||||
|
import android.support.test.filters.SmallTest;
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class SharedLogTest {
|
||||||
|
private static final String TIMESTAMP_PATTERN = "\\d{2}:\\d{2}:\\d{2}";
|
||||||
|
private static final String TIMESTAMP = "HH:MM:SS";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBasicOperation() {
|
||||||
|
final SharedLog logTop = new SharedLog("top");
|
||||||
|
logTop.mark("first post!");
|
||||||
|
|
||||||
|
final SharedLog logLevel2a = logTop.forSubComponent("twoA");
|
||||||
|
final SharedLog logLevel2b = logTop.forSubComponent("twoB");
|
||||||
|
logLevel2b.e("2b or not 2b");
|
||||||
|
logLevel2b.e("No exception", null);
|
||||||
|
logLevel2b.e("Wait, here's one", new Exception("Test"));
|
||||||
|
logLevel2a.w("second post?");
|
||||||
|
|
||||||
|
final SharedLog logLevel3 = logLevel2a.forSubComponent("three");
|
||||||
|
logTop.log("still logging");
|
||||||
|
logLevel3.log("3 >> 2");
|
||||||
|
logLevel2a.mark("ok: last post");
|
||||||
|
|
||||||
|
final String[] expected = {
|
||||||
|
" - MARK first post!",
|
||||||
|
" - [twoB] ERROR 2b or not 2b",
|
||||||
|
" - [twoB] ERROR No exception",
|
||||||
|
// No stacktrace in shared log, only in logcat
|
||||||
|
" - [twoB] ERROR Wait, here's one: Test",
|
||||||
|
" - [twoA] WARN second post?",
|
||||||
|
" - still logging",
|
||||||
|
" - [twoA.three] 3 >> 2",
|
||||||
|
" - [twoA] MARK ok: last post",
|
||||||
|
};
|
||||||
|
// Verify the logs are all there and in the correct order.
|
||||||
|
verifyLogLines(expected, logTop);
|
||||||
|
|
||||||
|
// In fact, because they all share the same underlying LocalLog,
|
||||||
|
// every subcomponent SharedLog's dump() is identical.
|
||||||
|
verifyLogLines(expected, logLevel2a);
|
||||||
|
verifyLogLines(expected, logLevel2b);
|
||||||
|
verifyLogLines(expected, logLevel3);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void verifyLogLines(String[] expected, SharedLog log) {
|
||||||
|
final ByteArrayOutputStream ostream = new ByteArrayOutputStream();
|
||||||
|
final PrintWriter pw = new PrintWriter(ostream, true);
|
||||||
|
log.dump(null, pw, null);
|
||||||
|
|
||||||
|
final String dumpOutput = ostream.toString();
|
||||||
|
assertTrue(dumpOutput != null);
|
||||||
|
assertTrue(!"".equals(dumpOutput));
|
||||||
|
|
||||||
|
final String[] lines = dumpOutput.split("\n");
|
||||||
|
assertEquals(expected.length, lines.length);
|
||||||
|
|
||||||
|
for (int i = 0; i < expected.length; i++) {
|
||||||
|
String got = lines[i];
|
||||||
|
String want = expected[i];
|
||||||
|
assertTrue(String.format("'%s' did not contain '%s'", got, want), got.endsWith(want));
|
||||||
|
assertTrue(String.format("'%s' did not contain a %s timestamp", got, TIMESTAMP),
|
||||||
|
got.replaceFirst(TIMESTAMP_PATTERN, TIMESTAMP).contains(TIMESTAMP));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1837,7 +1837,7 @@ public class Tethering extends BaseNetworkObserver {
|
|||||||
final TetherState tetherState = new TetherState(
|
final TetherState tetherState = new TetherState(
|
||||||
new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
|
new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
|
||||||
makeControlCallback(), mConfig.enableLegacyDhcpServer,
|
makeControlCallback(), mConfig.enableLegacyDhcpServer,
|
||||||
mDeps.getIpServerDependencies()));
|
mDeps.getIpServerDependencies(mContext)));
|
||||||
mTetherStates.put(iface, tetherState);
|
mTetherStates.put(iface, tetherState);
|
||||||
tetherState.ipServer.start();
|
tetherState.ipServer.start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,8 +60,8 @@ public class TetheringDependencies {
|
|||||||
/**
|
/**
|
||||||
* Get dependencies to be used by IpServer.
|
* Get dependencies to be used by IpServer.
|
||||||
*/
|
*/
|
||||||
public IpServer.Dependencies getIpServerDependencies() {
|
public IpServer.Dependencies getIpServerDependencies(Context context) {
|
||||||
return new IpServer.Dependencies();
|
return new IpServer.Dependencies(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,3 +2,19 @@ java_library_static {
|
|||||||
name: "services.net",
|
name: "services.net",
|
||||||
srcs: ["java/**/*.java"],
|
srcs: ["java/**/*.java"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: move to networking module with DhcpClient and remove lib
|
||||||
|
java_library {
|
||||||
|
name: "dhcp-packet-lib",
|
||||||
|
srcs: [
|
||||||
|
"java/android/net/dhcp/*Packet.java",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: move to networking module with IpNeighborMonitor/ConnectivityPacketTracker and remove lib
|
||||||
|
java_library {
|
||||||
|
name: "frameworks-net-shared-utils",
|
||||||
|
srcs: [
|
||||||
|
"java/android/net/util/FdEventsReader.java",
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
package android.net.dhcp;
|
package android.net.dhcp;
|
||||||
|
|
||||||
import static android.net.util.NetworkConstants.IPV4_MAX_MTU;
|
|
||||||
import static android.net.util.NetworkConstants.IPV4_MIN_MTU;
|
|
||||||
|
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.net.DhcpResults;
|
import android.net.DhcpResults;
|
||||||
import android.net.LinkAddress;
|
import android.net.LinkAddress;
|
||||||
@@ -37,6 +34,9 @@ import java.util.List;
|
|||||||
public abstract class DhcpPacket {
|
public abstract class DhcpPacket {
|
||||||
protected static final String TAG = "DhcpPacket";
|
protected static final String TAG = "DhcpPacket";
|
||||||
|
|
||||||
|
// TODO: use NetworkStackConstants.IPV4_MIN_MTU once this class is moved to the network stack.
|
||||||
|
private static final int IPV4_MIN_MTU = 68;
|
||||||
|
|
||||||
// dhcpcd has a minimum lease of 20 seconds, but DhcpStateMachine would refuse to wake up the
|
// dhcpcd has a minimum lease of 20 seconds, but DhcpStateMachine would refuse to wake up the
|
||||||
// CPU for anything shorter than 5 minutes. For sanity's sake, this must be higher than the
|
// CPU for anything shorter than 5 minutes. For sanity's sake, this must be higher than the
|
||||||
// DHCP client timeout.
|
// DHCP client timeout.
|
||||||
|
|||||||
@@ -17,20 +17,26 @@
|
|||||||
package android.net.ip;
|
package android.net.ip;
|
||||||
|
|
||||||
import static android.net.NetworkUtils.numericToInetAddress;
|
import static android.net.NetworkUtils.numericToInetAddress;
|
||||||
import static android.net.util.NetworkConstants.asByte;
|
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
|
||||||
import static android.net.util.NetworkConstants.FF;
|
import static android.net.util.NetworkConstants.FF;
|
||||||
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
|
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
|
||||||
|
import static android.net.util.NetworkConstants.asByte;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.INetd;
|
import android.net.INetd;
|
||||||
|
import android.net.INetworkStackStatusCallback;
|
||||||
import android.net.INetworkStatsService;
|
import android.net.INetworkStatsService;
|
||||||
import android.net.InterfaceConfiguration;
|
import android.net.InterfaceConfiguration;
|
||||||
import android.net.IpPrefix;
|
import android.net.IpPrefix;
|
||||||
import android.net.LinkAddress;
|
import android.net.LinkAddress;
|
||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
|
import android.net.NetworkStack;
|
||||||
import android.net.RouteInfo;
|
import android.net.RouteInfo;
|
||||||
import android.net.dhcp.DhcpServer;
|
import android.net.dhcp.DhcpServerCallbacks;
|
||||||
import android.net.dhcp.DhcpServingParams;
|
import android.net.dhcp.DhcpServingParamsParcel;
|
||||||
|
import android.net.dhcp.DhcpServingParamsParcelExt;
|
||||||
|
import android.net.dhcp.IDhcpServer;
|
||||||
import android.net.ip.RouterAdvertisementDaemon.RaParams;
|
import android.net.ip.RouterAdvertisementDaemon.RaParams;
|
||||||
import android.net.util.InterfaceParams;
|
import android.net.util.InterfaceParams;
|
||||||
import android.net.util.InterfaceSet;
|
import android.net.util.InterfaceSet;
|
||||||
@@ -126,6 +132,10 @@ public class IpServer extends StateMachine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Dependencies {
|
public static class Dependencies {
|
||||||
|
private final Context mContext;
|
||||||
|
public Dependencies(Context context) {
|
||||||
|
mContext = context;
|
||||||
|
}
|
||||||
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
|
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
|
||||||
return new RouterAdvertisementDaemon(ifParams);
|
return new RouterAdvertisementDaemon(ifParams);
|
||||||
}
|
}
|
||||||
@@ -138,9 +148,12 @@ public class IpServer extends StateMachine {
|
|||||||
return NetdService.getInstance();
|
return NetdService.getInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DhcpServer makeDhcpServer(Looper looper, String ifName,
|
/**
|
||||||
DhcpServingParams params, SharedLog log) {
|
* Create a DhcpServer instance to be used by IpServer.
|
||||||
return new DhcpServer(looper, ifName, params, log);
|
*/
|
||||||
|
public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
|
||||||
|
DhcpServerCallbacks cb) {
|
||||||
|
mContext.getSystemService(NetworkStack.class).makeDhcpServer(ifName, params, cb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +210,10 @@ public class IpServer extends StateMachine {
|
|||||||
// Advertisements (otherwise, we do not add them to mLinkProperties at all).
|
// Advertisements (otherwise, we do not add them to mLinkProperties at all).
|
||||||
private LinkProperties mLastIPv6LinkProperties;
|
private LinkProperties mLastIPv6LinkProperties;
|
||||||
private RouterAdvertisementDaemon mRaDaemon;
|
private RouterAdvertisementDaemon mRaDaemon;
|
||||||
private DhcpServer mDhcpServer;
|
|
||||||
|
// To be accessed only on the handler thread
|
||||||
|
private int mDhcpServerStartIndex = 0;
|
||||||
|
private IDhcpServer mDhcpServer;
|
||||||
private RaParams mLastRaParams;
|
private RaParams mLastRaParams;
|
||||||
|
|
||||||
public IpServer(
|
public IpServer(
|
||||||
@@ -252,35 +268,109 @@ public class IpServer extends StateMachine {
|
|||||||
|
|
||||||
private boolean startIPv4() { return configureIPv4(true); }
|
private boolean startIPv4() { return configureIPv4(true); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience wrapper around INetworkStackStatusCallback to run callbacks on the IpServer
|
||||||
|
* handler.
|
||||||
|
*
|
||||||
|
* <p>Different instances of this class can be created for each call to IDhcpServer methods,
|
||||||
|
* with different implementations of the callback, to differentiate handling of success/error in
|
||||||
|
* each call.
|
||||||
|
*/
|
||||||
|
private abstract class OnHandlerStatusCallback extends INetworkStackStatusCallback.Stub {
|
||||||
|
@Override
|
||||||
|
public void onStatusAvailable(int statusCode) {
|
||||||
|
getHandler().post(() -> callback(statusCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void callback(int statusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DhcpServerCallbacksImpl extends DhcpServerCallbacks {
|
||||||
|
private final int mStartIndex;
|
||||||
|
|
||||||
|
private DhcpServerCallbacksImpl(int startIndex) {
|
||||||
|
mStartIndex = startIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDhcpServerCreated(int statusCode, IDhcpServer server) throws RemoteException {
|
||||||
|
getHandler().post(() -> {
|
||||||
|
// We are on the handler thread: mDhcpServerStartIndex can be read safely.
|
||||||
|
if (mStartIndex != mDhcpServerStartIndex) {
|
||||||
|
// This start request is obsolete. When the |server| binder token goes out of
|
||||||
|
// scope, the garbage collector will finalize it, which causes the network stack
|
||||||
|
// process garbage collector to collect the server itself.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusCode != STATUS_SUCCESS) {
|
||||||
|
mLog.e("Error obtaining DHCP server: " + statusCode);
|
||||||
|
handleError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDhcpServer = server;
|
||||||
|
try {
|
||||||
|
mDhcpServer.start(new OnHandlerStatusCallback() {
|
||||||
|
@Override
|
||||||
|
public void callback(int startStatusCode) {
|
||||||
|
if (startStatusCode != STATUS_SUCCESS) {
|
||||||
|
mLog.e("Error starting DHCP server: " + startStatusCode);
|
||||||
|
handleError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleError() {
|
||||||
|
mLastError = ConnectivityManager.TETHER_ERROR_DHCPSERVER_ERROR;
|
||||||
|
transitionTo(mInitialState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean startDhcp(Inet4Address addr, int prefixLen) {
|
private boolean startDhcp(Inet4Address addr, int prefixLen) {
|
||||||
if (mUsingLegacyDhcp) {
|
if (mUsingLegacyDhcp) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
final DhcpServingParams params;
|
final DhcpServingParamsParcel params;
|
||||||
try {
|
params = new DhcpServingParamsParcelExt()
|
||||||
params = new DhcpServingParams.Builder()
|
.setDefaultRouters(addr)
|
||||||
.setDefaultRouters(addr)
|
.setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS)
|
||||||
.setDhcpLeaseTimeSecs(DHCP_LEASE_TIME_SECS)
|
.setDnsServers(addr)
|
||||||
.setDnsServers(addr)
|
.setServerAddr(new LinkAddress(addr, prefixLen))
|
||||||
.setServerAddr(new LinkAddress(addr, prefixLen))
|
.setMetered(true);
|
||||||
.setMetered(true)
|
// TODO: also advertise link MTU
|
||||||
.build();
|
|
||||||
// TODO: also advertise link MTU
|
|
||||||
} catch (DhcpServingParams.InvalidParameterException e) {
|
|
||||||
Log.e(TAG, "Invalid DHCP parameters", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mDhcpServer = mDeps.makeDhcpServer(getHandler().getLooper(), mIfaceName, params,
|
mDhcpServerStartIndex++;
|
||||||
mLog.forSubComponent("DHCP"));
|
mDeps.makeDhcpServer(
|
||||||
mDhcpServer.start();
|
mIfaceName, params, new DhcpServerCallbacksImpl(mDhcpServerStartIndex));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopDhcp() {
|
private void stopDhcp() {
|
||||||
|
// Make all previous start requests obsolete so servers are not started later
|
||||||
|
mDhcpServerStartIndex++;
|
||||||
|
|
||||||
if (mDhcpServer != null) {
|
if (mDhcpServer != null) {
|
||||||
mDhcpServer.stop();
|
try {
|
||||||
mDhcpServer = null;
|
mDhcpServer.stop(new OnHandlerStatusCallback() {
|
||||||
|
@Override
|
||||||
|
public void callback(int statusCode) {
|
||||||
|
if (statusCode != STATUS_SUCCESS) {
|
||||||
|
mLog.e("Error stopping DHCP server: " + statusCode);
|
||||||
|
mLastError = ConnectivityManager.TETHER_ERROR_DHCPSERVER_ERROR;
|
||||||
|
// Not much more we can do here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mDhcpServer = null;
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,9 +16,6 @@
|
|||||||
|
|
||||||
package android.net.util;
|
package android.net.util;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Networking protocol constants.
|
* Networking protocol constants.
|
||||||
*
|
*
|
||||||
@@ -81,8 +78,6 @@ public final class NetworkConstants {
|
|||||||
* - https://tools.ietf.org/html/rfc791
|
* - https://tools.ietf.org/html/rfc791
|
||||||
*/
|
*/
|
||||||
public static final int IPV4_HEADER_MIN_LEN = 20;
|
public static final int IPV4_HEADER_MIN_LEN = 20;
|
||||||
public static final int IPV4_MIN_MTU = 68;
|
|
||||||
public static final int IPV4_MAX_MTU = 65_535;
|
|
||||||
public static final int IPV4_IHL_MASK = 0xf;
|
public static final int IPV4_IHL_MASK = 0xf;
|
||||||
public static final int IPV4_FLAGS_OFFSET = 6;
|
public static final int IPV4_FLAGS_OFFSET = 6;
|
||||||
public static final int IPV4_FRAGMENT_MASK = 0x1fff;
|
public static final int IPV4_FRAGMENT_MASK = 0x1fff;
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import java.util.StringJoiner;
|
|||||||
*
|
*
|
||||||
* All access to class methods other than dump() must be on the same thread.
|
* All access to class methods other than dump() must be on the same thread.
|
||||||
*
|
*
|
||||||
|
* TODO: this is a copy of SharedLog in the NetworkStack. Remove after Tethering is migrated.
|
||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public class SharedLog {
|
public class SharedLog {
|
||||||
|
|||||||
@@ -22,20 +22,26 @@ import static android.net.ConnectivityManager.TETHERING_WIFI;
|
|||||||
import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
|
import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
|
||||||
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
|
||||||
import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
|
import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
|
||||||
|
import static android.net.NetworkUtils.intToInet4AddressHTH;
|
||||||
|
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
|
||||||
import static android.net.ip.IpServer.STATE_AVAILABLE;
|
import static android.net.ip.IpServer.STATE_AVAILABLE;
|
||||||
import static android.net.ip.IpServer.STATE_TETHERED;
|
import static android.net.ip.IpServer.STATE_TETHERED;
|
||||||
import static android.net.ip.IpServer.STATE_UNAVAILABLE;
|
import static android.net.ip.IpServer.STATE_UNAVAILABLE;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Matchers.anyString;
|
import static org.mockito.Matchers.anyString;
|
||||||
import static org.mockito.Matchers.eq;
|
import static org.mockito.Matchers.eq;
|
||||||
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.doThrow;
|
import static org.mockito.Mockito.doThrow;
|
||||||
import static org.mockito.Mockito.inOrder;
|
import static org.mockito.Mockito.inOrder;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.reset;
|
import static org.mockito.Mockito.reset;
|
||||||
|
import static org.mockito.Mockito.timeout;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
@@ -48,8 +54,9 @@ import android.net.LinkAddress;
|
|||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
import android.net.MacAddress;
|
import android.net.MacAddress;
|
||||||
import android.net.RouteInfo;
|
import android.net.RouteInfo;
|
||||||
import android.net.dhcp.DhcpServer;
|
import android.net.dhcp.DhcpServingParamsParcel;
|
||||||
import android.net.dhcp.DhcpServingParams;
|
import android.net.dhcp.IDhcpServer;
|
||||||
|
import android.net.dhcp.IDhcpServerCallbacks;
|
||||||
import android.net.util.InterfaceParams;
|
import android.net.util.InterfaceParams;
|
||||||
import android.net.util.InterfaceSet;
|
import android.net.util.InterfaceSet;
|
||||||
import android.net.util.SharedLog;
|
import android.net.util.SharedLog;
|
||||||
@@ -82,16 +89,18 @@ public class IpServerTest {
|
|||||||
private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams(
|
private static final InterfaceParams TEST_IFACE_PARAMS = new InterfaceParams(
|
||||||
IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
|
IFACE_NAME, 42 /* index */, MacAddress.ALL_ZEROS_ADDRESS, 1500 /* defaultMtu */);
|
||||||
|
|
||||||
|
private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
|
||||||
|
|
||||||
@Mock private INetworkManagementService mNMService;
|
@Mock private INetworkManagementService mNMService;
|
||||||
@Mock private INetworkStatsService mStatsService;
|
@Mock private INetworkStatsService mStatsService;
|
||||||
@Mock private IpServer.Callback mCallback;
|
@Mock private IpServer.Callback mCallback;
|
||||||
@Mock private InterfaceConfiguration mInterfaceConfiguration;
|
@Mock private InterfaceConfiguration mInterfaceConfiguration;
|
||||||
@Mock private SharedLog mSharedLog;
|
@Mock private SharedLog mSharedLog;
|
||||||
@Mock private DhcpServer mDhcpServer;
|
@Mock private IDhcpServer mDhcpServer;
|
||||||
@Mock private RouterAdvertisementDaemon mRaDaemon;
|
@Mock private RouterAdvertisementDaemon mRaDaemon;
|
||||||
@Mock private IpServer.Dependencies mDependencies;
|
@Mock private IpServer.Dependencies mDependencies;
|
||||||
|
|
||||||
@Captor private ArgumentCaptor<DhcpServingParams> mDhcpParamsCaptor;
|
@Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
|
||||||
|
|
||||||
private final TestLooper mLooper = new TestLooper();
|
private final TestLooper mLooper = new TestLooper();
|
||||||
private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
|
private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor =
|
||||||
@@ -112,8 +121,18 @@ public class IpServerTest {
|
|||||||
mLooper.dispatchAll();
|
mLooper.dispatchAll();
|
||||||
reset(mNMService, mStatsService, mCallback);
|
reset(mNMService, mStatsService, mCallback);
|
||||||
when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
|
when(mNMService.getInterfaceConfig(IFACE_NAME)).thenReturn(mInterfaceConfiguration);
|
||||||
when(mDependencies.makeDhcpServer(
|
|
||||||
any(), any(), mDhcpParamsCaptor.capture(), any())).thenReturn(mDhcpServer);
|
doAnswer(inv -> {
|
||||||
|
final IDhcpServerCallbacks cb = inv.getArgument(2);
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
}).run();
|
||||||
|
return null;
|
||||||
|
}).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any());
|
||||||
when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
|
when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
|
||||||
when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
|
when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
|
||||||
|
|
||||||
@@ -399,21 +418,20 @@ public class IpServerTest {
|
|||||||
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */);
|
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, true /* usingLegacyDhcp */);
|
||||||
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
|
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
|
||||||
|
|
||||||
verify(mDependencies, never()).makeDhcpServer(any(), any(), any(), any());
|
verify(mDependencies, never()).makeDhcpServer(any(), any(), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertDhcpStarted(IpPrefix expectedPrefix) {
|
private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
|
||||||
verify(mDependencies, times(1)).makeDhcpServer(
|
verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
|
||||||
eq(mLooper.getLooper()), eq(IFACE_NAME), any(), eq(mSharedLog));
|
verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).start(any());
|
||||||
verify(mDhcpServer, times(1)).start();
|
final DhcpServingParamsParcel params = mDhcpParamsCaptor.getValue();
|
||||||
final DhcpServingParams params = mDhcpParamsCaptor.getValue();
|
|
||||||
// Last address byte is random
|
// Last address byte is random
|
||||||
assertTrue(expectedPrefix.contains(params.serverAddr.getAddress()));
|
assertTrue(expectedPrefix.contains(intToInet4AddressHTH(params.serverAddr)));
|
||||||
assertEquals(expectedPrefix.getPrefixLength(), params.serverAddr.getPrefixLength());
|
assertEquals(expectedPrefix.getPrefixLength(), params.serverAddrPrefixLength);
|
||||||
assertEquals(1, params.defaultRouters.size());
|
assertEquals(1, params.defaultRouters.length);
|
||||||
assertEquals(params.serverAddr.getAddress(), params.defaultRouters.iterator().next());
|
assertEquals(params.serverAddr, params.defaultRouters[0]);
|
||||||
assertEquals(1, params.dnsServers.size());
|
assertEquals(1, params.dnsServers.length);
|
||||||
assertEquals(params.serverAddr.getAddress(), params.dnsServers.iterator().next());
|
assertEquals(params.serverAddr, params.dnsServers[0]);
|
||||||
assertEquals(DHCP_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs);
|
assertEquals(DHCP_LEASE_TIME_SECS, params.dhcpLeaseTimeSecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -458,7 +476,7 @@ public class IpServerTest {
|
|||||||
addr4 = addr;
|
addr4 = addr;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
assertTrue("missing IPv4 address", addr4 != null);
|
assertNotNull("missing IPv4 address", addr4);
|
||||||
|
|
||||||
// Assert the presence of the associated directly connected route.
|
// Assert the presence of the associated directly connected route.
|
||||||
final RouteInfo directlyConnected = new RouteInfo(addr4, null, lp.getInterfaceName());
|
final RouteInfo directlyConnected = new RouteInfo(addr4, null, lp.getInterfaceName());
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import static android.net.ConnectivityManager.TETHERING_USB;
|
|||||||
import static android.net.ConnectivityManager.TETHERING_WIFI;
|
import static android.net.ConnectivityManager.TETHERING_WIFI;
|
||||||
import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
|
import static android.net.ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
|
||||||
import static android.net.ConnectivityManager.TYPE_MOBILE;
|
import static android.net.ConnectivityManager.TYPE_MOBILE;
|
||||||
|
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
|
||||||
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
|
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
|
||||||
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
|
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE;
|
||||||
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
|
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
|
||||||
@@ -37,6 +38,7 @@ import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
|
|||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
import static org.mockito.ArgumentMatchers.argThat;
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
import static org.mockito.ArgumentMatchers.notNull;
|
import static org.mockito.ArgumentMatchers.notNull;
|
||||||
import static org.mockito.Matchers.anyInt;
|
import static org.mockito.Matchers.anyInt;
|
||||||
@@ -47,6 +49,8 @@ import static org.mockito.Mockito.atLeastOnce;
|
|||||||
import static org.mockito.Mockito.doThrow;
|
import static org.mockito.Mockito.doThrow;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.timeout;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
@@ -74,8 +78,9 @@ import android.net.NetworkInfo;
|
|||||||
import android.net.NetworkState;
|
import android.net.NetworkState;
|
||||||
import android.net.NetworkUtils;
|
import android.net.NetworkUtils;
|
||||||
import android.net.RouteInfo;
|
import android.net.RouteInfo;
|
||||||
import android.net.dhcp.DhcpServer;
|
import android.net.dhcp.DhcpServerCallbacks;
|
||||||
import android.net.dhcp.DhcpServingParams;
|
import android.net.dhcp.DhcpServingParamsParcel;
|
||||||
|
import android.net.dhcp.IDhcpServer;
|
||||||
import android.net.ip.IpServer;
|
import android.net.ip.IpServer;
|
||||||
import android.net.ip.RouterAdvertisementDaemon;
|
import android.net.ip.RouterAdvertisementDaemon;
|
||||||
import android.net.util.InterfaceParams;
|
import android.net.util.InterfaceParams;
|
||||||
@@ -86,7 +91,6 @@ import android.net.wifi.WifiManager;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.INetworkManagementService;
|
import android.os.INetworkManagementService;
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.PersistableBundle;
|
import android.os.PersistableBundle;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
@@ -129,6 +133,8 @@ public class TetheringTest {
|
|||||||
private static final String TEST_USB_IFNAME = "test_rndis0";
|
private static final String TEST_USB_IFNAME = "test_rndis0";
|
||||||
private static final String TEST_WLAN_IFNAME = "test_wlan0";
|
private static final String TEST_WLAN_IFNAME = "test_wlan0";
|
||||||
|
|
||||||
|
private static final int DHCPSERVER_START_TIMEOUT_MS = 1000;
|
||||||
|
|
||||||
@Mock private ApplicationInfo mApplicationInfo;
|
@Mock private ApplicationInfo mApplicationInfo;
|
||||||
@Mock private Context mContext;
|
@Mock private Context mContext;
|
||||||
@Mock private INetworkManagementService mNMService;
|
@Mock private INetworkManagementService mNMService;
|
||||||
@@ -143,9 +149,11 @@ public class TetheringTest {
|
|||||||
@Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
|
@Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
|
||||||
@Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
|
@Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
|
||||||
@Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
|
@Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
|
||||||
@Mock private DhcpServer mDhcpServer;
|
@Mock private IDhcpServer mDhcpServer;
|
||||||
@Mock private INetd mNetd;
|
@Mock private INetd mNetd;
|
||||||
|
|
||||||
|
private final MockIpServerDependencies mIpServerDependencies =
|
||||||
|
spy(new MockIpServerDependencies());
|
||||||
private final MockTetheringDependencies mTetheringDependencies =
|
private final MockTetheringDependencies mTetheringDependencies =
|
||||||
new MockTetheringDependencies();
|
new MockTetheringDependencies();
|
||||||
|
|
||||||
@@ -185,6 +193,47 @@ public class TetheringTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class MockIpServerDependencies extends IpServer.Dependencies {
|
||||||
|
MockIpServerDependencies() {
|
||||||
|
super(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
|
||||||
|
InterfaceParams ifParams) {
|
||||||
|
return mRouterAdvertisementDaemon;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InterfaceParams getInterfaceParams(String ifName) {
|
||||||
|
assertTrue("Non-mocked interface " + ifName,
|
||||||
|
ifName.equals(TEST_USB_IFNAME)
|
||||||
|
|| ifName.equals(TEST_WLAN_IFNAME)
|
||||||
|
|| ifName.equals(TEST_MOBILE_IFNAME));
|
||||||
|
final String[] ifaces = new String[] {
|
||||||
|
TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME };
|
||||||
|
return new InterfaceParams(ifName, ArrayUtils.indexOf(ifaces, ifName) + IFINDEX_OFFSET,
|
||||||
|
MacAddress.ALL_ZEROS_ADDRESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public INetd getNetdService() {
|
||||||
|
return mNetd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
|
||||||
|
DhcpServerCallbacks cb) {
|
||||||
|
new Thread(() -> {
|
||||||
|
try {
|
||||||
|
cb.onDhcpServerCreated(STATUS_SUCCESS, mDhcpServer);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
fail(e.getMessage());
|
||||||
|
}
|
||||||
|
}).run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public class MockTetheringDependencies extends TetheringDependencies {
|
public class MockTetheringDependencies extends TetheringDependencies {
|
||||||
StateMachine upstreamNetworkMonitorMasterSM;
|
StateMachine upstreamNetworkMonitorMasterSM;
|
||||||
ArrayList<IpServer> ipv6CoordinatorNotifyList;
|
ArrayList<IpServer> ipv6CoordinatorNotifyList;
|
||||||
@@ -216,35 +265,8 @@ public class TetheringTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IpServer.Dependencies getIpServerDependencies() {
|
public IpServer.Dependencies getIpServerDependencies(Context context) {
|
||||||
return new IpServer.Dependencies() {
|
return mIpServerDependencies;
|
||||||
@Override
|
|
||||||
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
|
|
||||||
InterfaceParams ifParams) {
|
|
||||||
return mRouterAdvertisementDaemon;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InterfaceParams getInterfaceParams(String ifName) {
|
|
||||||
final String[] ifaces = new String[] {
|
|
||||||
TEST_USB_IFNAME, TEST_WLAN_IFNAME, TEST_MOBILE_IFNAME };
|
|
||||||
final int index = ArrayUtils.indexOf(ifaces, ifName);
|
|
||||||
assertTrue("Non-mocked interface: " + ifName, index >= 0);
|
|
||||||
return new InterfaceParams(ifName, index + IFINDEX_OFFSET,
|
|
||||||
MacAddress.ALL_ZEROS_ADDRESS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public INetd getNetdService() {
|
|
||||||
return mNetd;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DhcpServer makeDhcpServer(Looper looper, String ifName,
|
|
||||||
DhcpServingParams params, SharedLog log) {
|
|
||||||
return mDhcpServer;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -546,7 +568,7 @@ public class TetheringTest {
|
|||||||
|
|
||||||
sendIPv6TetherUpdates(upstreamState);
|
sendIPv6TetherUpdates(upstreamState);
|
||||||
verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
|
verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
|
||||||
verify(mDhcpServer, times(1)).start();
|
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -557,7 +579,7 @@ public class TetheringTest {
|
|||||||
runUsbTethering(upstreamState);
|
runUsbTethering(upstreamState);
|
||||||
sendIPv6TetherUpdates(upstreamState);
|
sendIPv6TetherUpdates(upstreamState);
|
||||||
|
|
||||||
verify(mDhcpServer, never()).start();
|
verify(mIpServerDependencies, never()).makeDhcpServer(any(), any(), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -581,7 +603,7 @@ public class TetheringTest {
|
|||||||
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||||
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||||
verify(mRouterAdvertisementDaemon, times(1)).start();
|
verify(mRouterAdvertisementDaemon, times(1)).start();
|
||||||
verify(mDhcpServer, times(1)).start();
|
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
|
||||||
|
|
||||||
sendIPv6TetherUpdates(upstreamState);
|
sendIPv6TetherUpdates(upstreamState);
|
||||||
verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
|
verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
|
||||||
@@ -595,7 +617,7 @@ public class TetheringTest {
|
|||||||
|
|
||||||
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
|
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
|
||||||
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||||
verify(mDhcpServer, times(1)).start();
|
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
|
||||||
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||||
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
|
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME,
|
||||||
TEST_XLAT_MOBILE_IFNAME);
|
TEST_XLAT_MOBILE_IFNAME);
|
||||||
@@ -612,7 +634,7 @@ public class TetheringTest {
|
|||||||
runUsbTethering(upstreamState);
|
runUsbTethering(upstreamState);
|
||||||
|
|
||||||
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||||
verify(mDhcpServer, times(1)).start();
|
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
|
||||||
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||||
|
|
||||||
// Then 464xlat comes up
|
// Then 464xlat comes up
|
||||||
@@ -636,7 +658,7 @@ public class TetheringTest {
|
|||||||
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||||
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
|
||||||
// DHCP not restarted on downstream (still times(1))
|
// DHCP not restarted on downstream (still times(1))
|
||||||
verify(mDhcpServer, times(1)).start();
|
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
Reference in New Issue
Block a user