Merge changes from topic 'tether-offload-cherrypick'
* changes: Add code to dump OffloadController state. Fetch tethering offload stats. Allow more than one source of tethering statistics.
This commit is contained in:
@@ -221,6 +221,7 @@ LOCAL_SRC_FILES += \
|
||||
core/java/android/net/INetworkScoreService.aidl \
|
||||
core/java/android/net/INetworkStatsService.aidl \
|
||||
core/java/android/net/INetworkStatsSession.aidl \
|
||||
core/java/android/net/ITetheringStatsProvider.aidl \
|
||||
core/java/android/net/nsd/INsdManager.aidl \
|
||||
core/java/android/nfc/IAppCallback.aidl \
|
||||
core/java/android/nfc/INfcAdapter.aidl \
|
||||
|
||||
33
core/java/android/net/ITetheringStatsProvider.aidl
Normal file
33
core/java/android/net/ITetheringStatsProvider.aidl
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import android.net.NetworkStats;
|
||||
|
||||
/**
|
||||
* Interface that allows NetworkManagementService to query for tethering statistics.
|
||||
*
|
||||
* TODO: this does not really need to be an interface since Tethering runs in the same process
|
||||
* as NetworkManagementService. Consider refactoring Tethering to use direct access to
|
||||
* NetworkManagementService instead of using INetworkManagementService, and then deleting this
|
||||
* interface.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
interface ITetheringStatsProvider {
|
||||
NetworkStats getTetherStats();
|
||||
}
|
||||
@@ -20,6 +20,7 @@ package android.os;
|
||||
import android.net.InterfaceConfiguration;
|
||||
import android.net.INetd;
|
||||
import android.net.INetworkManagementEventObserver;
|
||||
import android.net.ITetheringStatsProvider;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkStats;
|
||||
import android.net.RouteInfo;
|
||||
@@ -206,6 +207,18 @@ interface INetworkManagementService
|
||||
*/
|
||||
void disableNat(String internalInterface, String externalInterface);
|
||||
|
||||
/**
|
||||
* Registers a {@code ITetheringStatsProvider} to provide tethering statistics.
|
||||
* All registered providers will be called in order, and their results will be added together.
|
||||
* Netd is always registered as a tethering stats provider.
|
||||
*/
|
||||
void registerTetheringStatsProvider(ITetheringStatsProvider provider, String name);
|
||||
|
||||
/**
|
||||
* Unregisters a previously-registered {@code ITetheringStatsProvider}.
|
||||
*/
|
||||
void unregisterTetheringStatsProvider(ITetheringStatsProvider provider);
|
||||
|
||||
/**
|
||||
** PPPD
|
||||
**/
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.server;
|
||||
|
||||
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
|
||||
import static android.Manifest.permission.DUMP;
|
||||
import static android.Manifest.permission.NETWORK_STACK;
|
||||
import static android.Manifest.permission.SHUTDOWN;
|
||||
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
|
||||
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
|
||||
@@ -53,6 +54,7 @@ import android.content.Context;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.INetd;
|
||||
import android.net.INetworkManagementEventObserver;
|
||||
import android.net.ITetheringStatsProvider;
|
||||
import android.net.InterfaceConfiguration;
|
||||
import android.net.IpPrefix;
|
||||
import android.net.LinkAddress;
|
||||
@@ -220,6 +222,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub
|
||||
|
||||
private final NetworkStatsFactory mStatsFactory = new NetworkStatsFactory();
|
||||
|
||||
@GuardedBy("mTetheringStatsProviders")
|
||||
private final HashMap<ITetheringStatsProvider, String>
|
||||
mTetheringStatsProviders = Maps.newHashMap();
|
||||
|
||||
private final Object mQuotaLock = new Object();
|
||||
|
||||
/** Set of interfaces with active quotas. */
|
||||
@@ -319,6 +325,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub
|
||||
|
||||
// Add ourself to the Watchdog monitors.
|
||||
Watchdog.getInstance().addMonitor(this);
|
||||
|
||||
synchronized (mTetheringStatsProviders) {
|
||||
mTetheringStatsProviders.put(new NetdTetheringStatsProvider(), "netd");
|
||||
}
|
||||
}
|
||||
|
||||
static NetworkManagementService create(Context context, String socket)
|
||||
@@ -499,6 +509,23 @@ public class NetworkManagementService extends INetworkManagementService.Stub
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerTetheringStatsProvider(ITetheringStatsProvider provider, String name) {
|
||||
mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG);
|
||||
Preconditions.checkNotNull(provider);
|
||||
synchronized(mTetheringStatsProviders) {
|
||||
mTetheringStatsProviders.put(provider, name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterTetheringStatsProvider(ITetheringStatsProvider provider) {
|
||||
mContext.enforceCallingOrSelfPermission(NETWORK_STACK, TAG);
|
||||
synchronized(mTetheringStatsProviders) {
|
||||
mTetheringStatsProviders.remove(provider);
|
||||
}
|
||||
}
|
||||
|
||||
// Sync the state of the given chain with the native daemon.
|
||||
private void syncFirewallChainLocked(int chain, SparseIntArray uidFirewallRules, String name) {
|
||||
int size = uidFirewallRules.size();
|
||||
@@ -1748,14 +1775,16 @@ public class NetworkManagementService extends INetworkManagementService.Stub
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetworkStats getNetworkStatsTethering() {
|
||||
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
|
||||
|
||||
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
|
||||
try {
|
||||
final NativeDaemonEvent[] events = mConnector.executeForList(
|
||||
"bandwidth", "gettetherstats");
|
||||
private class NetdTetheringStatsProvider extends ITetheringStatsProvider.Stub {
|
||||
@Override
|
||||
public NetworkStats getTetherStats() {
|
||||
final NativeDaemonEvent[] events;
|
||||
try {
|
||||
events = mConnector.executeForList("bandwidth", "gettetherstats");
|
||||
} catch (NativeDaemonConnectorException e) {
|
||||
throw e.rethrowAsParcelableException();
|
||||
}
|
||||
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
|
||||
for (NativeDaemonEvent event : events) {
|
||||
if (event.getCode() != TetheringStatsListResult) continue;
|
||||
|
||||
@@ -1781,8 +1810,24 @@ public class NetworkManagementService extends INetworkManagementService.Stub
|
||||
throw new IllegalStateException("problem parsing tethering stats: " + event);
|
||||
}
|
||||
}
|
||||
} catch (NativeDaemonConnectorException e) {
|
||||
throw e.rethrowAsParcelableException();
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NetworkStats getNetworkStatsTethering() {
|
||||
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
|
||||
|
||||
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
|
||||
synchronized (mTetheringStatsProviders) {
|
||||
for (ITetheringStatsProvider provider: mTetheringStatsProviders.keySet()) {
|
||||
try {
|
||||
stats.combineAllValues(provider.getTetherStats());
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Problem reading tethering stats from " +
|
||||
mTetheringStatsProviders.get(provider) + ": " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ public class Tethering extends BaseNetworkObserver {
|
||||
final Handler smHandler = mTetherMasterSM.getHandler();
|
||||
mOffloadController = new OffloadController(smHandler,
|
||||
deps.getOffloadHardwareInterface(smHandler, mLog),
|
||||
mContext.getContentResolver(),
|
||||
mContext.getContentResolver(), mNMService,
|
||||
mLog);
|
||||
mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(
|
||||
mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
|
||||
@@ -1755,6 +1755,11 @@ public class Tethering extends BaseNetworkObserver {
|
||||
pw.decreaseIndent();
|
||||
}
|
||||
|
||||
pw.println("Hardware offload:");
|
||||
pw.increaseIndent();
|
||||
mOffloadController.dump(pw);
|
||||
pw.decreaseIndent();
|
||||
|
||||
pw.println("Log:");
|
||||
pw.increaseIndent();
|
||||
if (argsContain(args, SHORT_ARG)) {
|
||||
|
||||
@@ -16,24 +16,38 @@
|
||||
|
||||
package com.android.server.connectivity.tethering;
|
||||
|
||||
import static android.net.NetworkStats.SET_DEFAULT;
|
||||
import static android.net.NetworkStats.TAG_NONE;
|
||||
import static android.net.TrafficStats.UID_TETHERING;
|
||||
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.net.ITetheringStatsProvider;
|
||||
import android.net.IpPrefix;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.NetworkStats;
|
||||
import android.net.RouteInfo;
|
||||
import android.net.util.SharedLog;
|
||||
import android.os.Handler;
|
||||
import android.os.INetworkManagementService;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemClock;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* A class to encapsulate the business logic of programming the tethering
|
||||
@@ -44,6 +58,8 @@ import java.util.Set;
|
||||
public class OffloadController {
|
||||
private static final String TAG = OffloadController.class.getSimpleName();
|
||||
|
||||
private static final int STATS_FETCH_TIMEOUT_MS = 1000;
|
||||
|
||||
private final Handler mHandler;
|
||||
private final OffloadHardwareInterface mHwInterface;
|
||||
private final ContentResolver mContentResolver;
|
||||
@@ -59,14 +75,25 @@ public class OffloadController {
|
||||
// prefixes representing only the locally-assigned IP addresses.
|
||||
private Set<String> mLastLocalPrefixStrs;
|
||||
|
||||
// Maps upstream interface names to offloaded traffic statistics.
|
||||
private HashMap<String, OffloadHardwareInterface.ForwardedStats>
|
||||
mForwardedStats = new HashMap<>();
|
||||
|
||||
public OffloadController(Handler h, OffloadHardwareInterface hwi,
|
||||
ContentResolver contentResolver, SharedLog log) {
|
||||
ContentResolver contentResolver, INetworkManagementService nms, SharedLog log) {
|
||||
mHandler = h;
|
||||
mHwInterface = hwi;
|
||||
mContentResolver = contentResolver;
|
||||
mLog = log.forSubComponent(TAG);
|
||||
mExemptPrefixes = new HashSet<>();
|
||||
mLastLocalPrefixStrs = new HashSet<>();
|
||||
|
||||
try {
|
||||
nms.registerTetheringStatsProvider(
|
||||
new OffloadTetheringStatsProvider(), getClass().getSimpleName());
|
||||
} catch (RemoteException e) {
|
||||
mLog.e("Cannot register offload stats provider: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
@@ -138,6 +165,7 @@ public class OffloadController {
|
||||
|
||||
public void stop() {
|
||||
final boolean wasStarted = started();
|
||||
updateStatsForCurrentUpstream();
|
||||
mUpstreamLinkProperties = null;
|
||||
mHwInterface.stopOffloadControl();
|
||||
mControlInitialized = false;
|
||||
@@ -145,16 +173,76 @@ public class OffloadController {
|
||||
if (wasStarted) mLog.log("tethering offload stopped");
|
||||
}
|
||||
|
||||
private class OffloadTetheringStatsProvider extends ITetheringStatsProvider.Stub {
|
||||
@Override
|
||||
public NetworkStats getTetherStats() {
|
||||
NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
mHandler.post(() -> {
|
||||
try {
|
||||
NetworkStats.Entry entry = new NetworkStats.Entry();
|
||||
entry.set = SET_DEFAULT;
|
||||
entry.tag = TAG_NONE;
|
||||
entry.uid = UID_TETHERING;
|
||||
|
||||
updateStatsForCurrentUpstream();
|
||||
|
||||
for (String iface : mForwardedStats.keySet()) {
|
||||
entry.iface = iface;
|
||||
entry.rxBytes = mForwardedStats.get(iface).rxBytes;
|
||||
entry.txBytes = mForwardedStats.get(iface).txBytes;
|
||||
stats.addValues(entry);
|
||||
}
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
latch.await(STATS_FETCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
mLog.e("Tethering stats fetch timed out after " + STATS_FETCH_TIMEOUT_MS + "ms");
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeUpdateStats(String iface) {
|
||||
if (TextUtils.isEmpty(iface)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mForwardedStats.containsKey(iface)) {
|
||||
mForwardedStats.put(iface, new OffloadHardwareInterface.ForwardedStats());
|
||||
}
|
||||
mForwardedStats.get(iface).add(mHwInterface.getForwardedStats(iface));
|
||||
}
|
||||
|
||||
private void updateStatsForCurrentUpstream() {
|
||||
if (mUpstreamLinkProperties != null) {
|
||||
maybeUpdateStats(mUpstreamLinkProperties.getInterfaceName());
|
||||
}
|
||||
}
|
||||
|
||||
public void setUpstreamLinkProperties(LinkProperties lp) {
|
||||
if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return;
|
||||
|
||||
String prevUpstream = (mUpstreamLinkProperties != null) ?
|
||||
mUpstreamLinkProperties.getInterfaceName() : null;
|
||||
|
||||
mUpstreamLinkProperties = (lp != null) ? new LinkProperties(lp) : null;
|
||||
|
||||
// TODO: examine return code and decide what to do if programming
|
||||
// upstream parameters fails (probably just wait for a subsequent
|
||||
// onOffloadEvent() callback to tell us offload is available again and
|
||||
// then reapply all state).
|
||||
computeAndPushLocalPrefixes();
|
||||
pushUpstreamParameters();
|
||||
|
||||
// Update stats after we've told the hardware to change routing so we don't miss packets.
|
||||
maybeUpdateStats(prevUpstream);
|
||||
}
|
||||
|
||||
public void setLocalPrefixes(Set<IpPrefix> localPrefixes) {
|
||||
@@ -262,4 +350,16 @@ public class OffloadController {
|
||||
for (IpPrefix pfx : prefixSet) localPrefixStrs.add(pfx.toString());
|
||||
return localPrefixStrs;
|
||||
}
|
||||
|
||||
public void dump(IndentingPrintWriter pw) {
|
||||
if (isOffloadDisabled()) {
|
||||
pw.println("Offload disabled");
|
||||
return;
|
||||
}
|
||||
pw.println("Offload HALs " + (started() ? "started" : "not started"));
|
||||
LinkProperties lp = mUpstreamLinkProperties;
|
||||
String upstream = (lp != null) ? lp.getInterfaceName() : null;
|
||||
pw.println("Current upstream: " + upstream);
|
||||
pw.println("Exempt prefixes: " + mLastLocalPrefixStrs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,6 +178,7 @@ public class TetheringTest {
|
||||
mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager,
|
||||
mLooper.getLooper(), mSystemProperties,
|
||||
mTetheringDependencies);
|
||||
verify(mNMService).registerTetheringStatsProvider(any(), anyString());
|
||||
}
|
||||
|
||||
@After
|
||||
|
||||
@@ -16,26 +16,38 @@
|
||||
|
||||
package com.android.server.connectivity.tethering;
|
||||
|
||||
import static android.net.NetworkStats.SET_DEFAULT;
|
||||
import static android.net.NetworkStats.TAG_NONE;
|
||||
import static android.net.TrafficStats.UID_TETHERING;
|
||||
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
|
||||
import static com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.net.ITetheringStatsProvider;
|
||||
import android.net.IpPrefix;
|
||||
import android.net.LinkAddress;
|
||||
import android.net.LinkProperties;
|
||||
import android.net.NetworkStats;
|
||||
import android.net.RouteInfo;
|
||||
import android.net.util.SharedLog;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.INetworkManagementService;
|
||||
import android.provider.Settings;
|
||||
import android.provider.Settings.SettingNotFoundException;
|
||||
|
||||
@@ -66,11 +78,14 @@ public class OffloadControllerTest {
|
||||
@Mock private OffloadHardwareInterface mHardware;
|
||||
@Mock private ApplicationInfo mApplicationInfo;
|
||||
@Mock private Context mContext;
|
||||
@Mock private INetworkManagementService mNMService;
|
||||
private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
|
||||
ArgumentCaptor.forClass(ArrayList.class);
|
||||
private final ArgumentCaptor<ITetheringStatsProvider.Stub> mTetherStatsProviderCaptor =
|
||||
ArgumentCaptor.forClass(ITetheringStatsProvider.Stub.class);
|
||||
private MockContentResolver mContentResolver;
|
||||
|
||||
@Before public void setUp() throws Exception {
|
||||
@Before public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mContext.getApplicationInfo()).thenReturn(mApplicationInfo);
|
||||
when(mContext.getPackageName()).thenReturn("OffloadControllerTest");
|
||||
@@ -90,15 +105,24 @@ public class OffloadControllerTest {
|
||||
when(mHardware.initOffloadConfig()).thenReturn(true);
|
||||
when(mHardware.initOffloadControl(any(OffloadHardwareInterface.ControlCallback.class)))
|
||||
.thenReturn(true);
|
||||
when(mHardware.getForwardedStats(any())).thenReturn(new ForwardedStats());
|
||||
}
|
||||
|
||||
private void enableOffload() {
|
||||
Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0);
|
||||
}
|
||||
|
||||
private OffloadController makeOffloadController() throws Exception {
|
||||
OffloadController offload = new OffloadController(new Handler(Looper.getMainLooper()),
|
||||
mHardware, mContentResolver, mNMService, new SharedLog("test"));
|
||||
verify(mNMService).registerTetheringStatsProvider(
|
||||
mTetherStatsProviderCaptor.capture(), anyString());
|
||||
return offload;
|
||||
}
|
||||
|
||||
// TODO: Restore when FakeSettingsProvider.clearSettingsProvider() is available.
|
||||
// @Test
|
||||
public void testNoSettingsValueDefaultDisabledDoesNotStart() {
|
||||
public void testNoSettingsValueDefaultDisabledDoesNotStart() throws Exception {
|
||||
setupFunctioningHardwareInterface();
|
||||
when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(1);
|
||||
try {
|
||||
@@ -106,8 +130,7 @@ public class OffloadControllerTest {
|
||||
fail();
|
||||
} catch (SettingNotFoundException expected) {}
|
||||
|
||||
final OffloadController offload =
|
||||
new OffloadController(null, mHardware, mContentResolver, new SharedLog("test"));
|
||||
final OffloadController offload = makeOffloadController();
|
||||
offload.start();
|
||||
|
||||
final InOrder inOrder = inOrder(mHardware);
|
||||
@@ -120,7 +143,7 @@ public class OffloadControllerTest {
|
||||
|
||||
// TODO: Restore when FakeSettingsProvider.clearSettingsProvider() is available.
|
||||
// @Test
|
||||
public void testNoSettingsValueDefaultEnabledDoesStart() {
|
||||
public void testNoSettingsValueDefaultEnabledDoesStart() throws Exception {
|
||||
setupFunctioningHardwareInterface();
|
||||
when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(0);
|
||||
try {
|
||||
@@ -128,8 +151,7 @@ public class OffloadControllerTest {
|
||||
fail();
|
||||
} catch (SettingNotFoundException expected) {}
|
||||
|
||||
final OffloadController offload =
|
||||
new OffloadController(null, mHardware, mContentResolver, new SharedLog("test"));
|
||||
final OffloadController offload = makeOffloadController();
|
||||
offload.start();
|
||||
|
||||
final InOrder inOrder = inOrder(mHardware);
|
||||
@@ -141,12 +163,11 @@ public class OffloadControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSettingsAllowsStart() {
|
||||
public void testSettingsAllowsStart() throws Exception {
|
||||
setupFunctioningHardwareInterface();
|
||||
Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0);
|
||||
|
||||
final OffloadController offload =
|
||||
new OffloadController(null, mHardware, mContentResolver, new SharedLog("test"));
|
||||
final OffloadController offload = makeOffloadController();
|
||||
offload.start();
|
||||
|
||||
final InOrder inOrder = inOrder(mHardware);
|
||||
@@ -158,12 +179,11 @@ public class OffloadControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSettingsDisablesStart() {
|
||||
public void testSettingsDisablesStart() throws Exception {
|
||||
setupFunctioningHardwareInterface();
|
||||
Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 1);
|
||||
|
||||
final OffloadController offload =
|
||||
new OffloadController(null, mHardware, mContentResolver, new SharedLog("test"));
|
||||
final OffloadController offload = makeOffloadController();
|
||||
offload.start();
|
||||
|
||||
final InOrder inOrder = inOrder(mHardware);
|
||||
@@ -178,8 +198,7 @@ public class OffloadControllerTest {
|
||||
setupFunctioningHardwareInterface();
|
||||
enableOffload();
|
||||
|
||||
final OffloadController offload =
|
||||
new OffloadController(null, mHardware, mContentResolver, new SharedLog("test"));
|
||||
final OffloadController offload = makeOffloadController();
|
||||
offload.start();
|
||||
|
||||
final InOrder inOrder = inOrder(mHardware);
|
||||
@@ -244,6 +263,7 @@ public class OffloadControllerTest {
|
||||
inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
|
||||
inOrder.verify(mHardware, times(1)).setUpstreamParameters(
|
||||
eq(testIfName), eq(ipv4Addr), eq(null), eq(null));
|
||||
inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName));
|
||||
inOrder.verifyNoMoreInteractions();
|
||||
|
||||
final String ipv4Gateway = "192.0.2.1";
|
||||
@@ -253,6 +273,7 @@ public class OffloadControllerTest {
|
||||
inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
|
||||
inOrder.verify(mHardware, times(1)).setUpstreamParameters(
|
||||
eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), eq(null));
|
||||
inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName));
|
||||
inOrder.verifyNoMoreInteractions();
|
||||
|
||||
final String ipv6Gw1 = "fe80::cafe";
|
||||
@@ -262,6 +283,7 @@ public class OffloadControllerTest {
|
||||
inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
|
||||
inOrder.verify(mHardware, times(1)).setUpstreamParameters(
|
||||
eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture());
|
||||
inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName));
|
||||
ArrayList<String> v6gws = mStringArrayCaptor.getValue();
|
||||
assertEquals(1, v6gws.size());
|
||||
assertTrue(v6gws.contains(ipv6Gw1));
|
||||
@@ -274,6 +296,7 @@ public class OffloadControllerTest {
|
||||
inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
|
||||
inOrder.verify(mHardware, times(1)).setUpstreamParameters(
|
||||
eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture());
|
||||
inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName));
|
||||
v6gws = mStringArrayCaptor.getValue();
|
||||
assertEquals(2, v6gws.size());
|
||||
assertTrue(v6gws.contains(ipv6Gw1));
|
||||
@@ -291,6 +314,7 @@ public class OffloadControllerTest {
|
||||
inOrder.verify(mHardware, never()).setLocalPrefixes(mStringArrayCaptor.capture());
|
||||
inOrder.verify(mHardware, times(1)).setUpstreamParameters(
|
||||
eq(testIfName), eq(ipv4Addr), eq(ipv4Gateway), mStringArrayCaptor.capture());
|
||||
inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName));
|
||||
v6gws = mStringArrayCaptor.getValue();
|
||||
assertEquals(2, v6gws.size());
|
||||
assertTrue(v6gws.contains(ipv6Gw1));
|
||||
@@ -325,6 +349,7 @@ public class OffloadControllerTest {
|
||||
assertEquals(2, v6gws.size());
|
||||
assertTrue(v6gws.contains(ipv6Gw1));
|
||||
assertTrue(v6gws.contains(ipv6Gw2));
|
||||
inOrder.verify(mHardware, times(1)).getForwardedStats(eq(testIfName));
|
||||
inOrder.verifyNoMoreInteractions();
|
||||
|
||||
// Completely identical LinkProperties updates are de-duped.
|
||||
@@ -335,4 +360,65 @@ public class OffloadControllerTest {
|
||||
anyObject(), anyObject(), anyObject(), anyObject());
|
||||
inOrder.verifyNoMoreInteractions();
|
||||
}
|
||||
|
||||
private void assertNetworkStats(String iface, ForwardedStats stats, NetworkStats.Entry entry) {
|
||||
assertEquals(iface, entry.iface);
|
||||
assertEquals(stats.rxBytes, entry.rxBytes);
|
||||
assertEquals(stats.txBytes, entry.txBytes);
|
||||
assertEquals(SET_DEFAULT, entry.set);
|
||||
assertEquals(TAG_NONE, entry.tag);
|
||||
assertEquals(UID_TETHERING, entry.uid);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetForwardedStats() throws Exception {
|
||||
setupFunctioningHardwareInterface();
|
||||
enableOffload();
|
||||
|
||||
final OffloadController offload = makeOffloadController();
|
||||
offload.start();
|
||||
|
||||
final String ethernetIface = "eth1";
|
||||
final String mobileIface = "rmnet_data0";
|
||||
|
||||
ForwardedStats ethernetStats = new ForwardedStats();
|
||||
ethernetStats.rxBytes = 12345;
|
||||
ethernetStats.txBytes = 54321;
|
||||
|
||||
ForwardedStats mobileStats = new ForwardedStats();
|
||||
mobileStats.rxBytes = 999;
|
||||
mobileStats.txBytes = 99999;
|
||||
|
||||
when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats);
|
||||
when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn(mobileStats);
|
||||
|
||||
final LinkProperties lp = new LinkProperties();
|
||||
lp.setInterfaceName(ethernetIface);
|
||||
offload.setUpstreamLinkProperties(lp);
|
||||
|
||||
lp.setInterfaceName(mobileIface);
|
||||
offload.setUpstreamLinkProperties(lp);
|
||||
|
||||
lp.setInterfaceName(ethernetIface);
|
||||
offload.setUpstreamLinkProperties(lp);
|
||||
|
||||
ethernetStats.rxBytes = 100000;
|
||||
ethernetStats.txBytes = 100000;
|
||||
offload.setUpstreamLinkProperties(null);
|
||||
|
||||
NetworkStats stats = mTetherStatsProviderCaptor.getValue().getTetherStats();
|
||||
assertEquals(2, stats.size());
|
||||
|
||||
NetworkStats.Entry entry = null;
|
||||
int ethernetPosition = ethernetIface.equals(stats.getValues(0, entry).iface) ? 0 : 1;
|
||||
int mobilePosition = 1 - ethernetPosition;
|
||||
|
||||
entry = stats.getValues(mobilePosition, entry);
|
||||
assertNetworkStats(mobileIface, mobileStats, entry);
|
||||
|
||||
ethernetStats.rxBytes = 12345 + 100000;
|
||||
ethernetStats.txBytes = 54321 + 100000;
|
||||
entry = stats.getValues(ethernetPosition, entry);
|
||||
assertNetworkStats(ethernetIface, ethernetStats, entry);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user