Merge "Tethering: add p2p regex for tethering modes"
diff --git a/Tethering/AndroidManifest.xml b/Tethering/AndroidManifest.xml
index 5a71eb2..c71d0d7 100644
--- a/Tethering/AndroidManifest.xml
+++ b/Tethering/AndroidManifest.xml
@@ -36,6 +36,7 @@
<uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
<uses-permission android:name="android.permission.TETHER_PRIVILEGED" />
<uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
+ <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<application
diff --git a/Tethering/res/values/config.xml b/Tethering/res/values/config.xml
index d61f8e1..19e8d69 100644
--- a/Tethering/res/values/config.xml
+++ b/Tethering/res/values/config.xml
@@ -101,7 +101,7 @@
<!-- If the mobile hotspot feature requires provisioning, a package name and class name
can be provided to launch a supported application that provisions the devices.
- EntitlementManager will send an inent to Settings with the specified package name and
+ EntitlementManager will send an intent to Settings with the specified package name and
class name in extras to launch provision app.
TODO: note what extras here.
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index 6ac467e..4306cf0 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -26,7 +26,6 @@
import android.net.INetd;
import android.net.INetworkStackStatusCallback;
-import android.net.INetworkStatsService;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -176,7 +175,6 @@
private final SharedLog mLog;
private final INetd mNetd;
- private final INetworkStatsService mStatsService;
private final Callback mCallback;
private final InterfaceController mInterfaceCtrl;
@@ -208,12 +206,10 @@
public IpServer(
String ifaceName, Looper looper, int interfaceType, SharedLog log,
- INetd netd, INetworkStatsService statsService, Callback callback,
- boolean usingLegacyDhcp, Dependencies deps) {
+ INetd netd, Callback callback, boolean usingLegacyDhcp, Dependencies deps) {
super(ifaceName, looper);
mLog = log.forSubComponent(ifaceName);
mNetd = netd;
- mStatsService = statsService;
mCallback = callback;
mInterfaceCtrl = new InterfaceController(ifaceName, mNetd, mLog);
mIfaceName = ifaceName;
@@ -882,12 +878,6 @@
// to remove their rules, which generates errors.
// Just do the best we can.
try {
- // About to tear down NAT; gather remaining statistics.
- mStatsService.forceUpdate();
- } catch (Exception e) {
- mLog.e("Exception in forceUpdate: " + e.toString());
- }
- try {
mNetd.ipfwdRemoveInterfaceForward(mIfaceName, upstreamIface);
} catch (RemoteException | ServiceSpecificException e) {
mLog.e("Exception in ipfwdRemoveInterfaceForward: " + e.toString());
diff --git a/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java b/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
index 4e2a2c1..1cabc8d 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -27,8 +27,6 @@
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_PROVISION_FAILED;
-import static com.android.internal.R.string.config_wifi_tether_enable;
-
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
@@ -36,7 +34,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.res.Resources;
import android.net.util.SharedLog;
import android.os.Bundle;
import android.os.Handler;
@@ -54,6 +51,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.StateMachine;
+import com.android.networkstack.tethering.R;
import java.io.PrintWriter;
@@ -75,9 +73,7 @@
"com.android.server.connectivity.tethering.PROVISIONING_RECHECK_ALARM";
private static final String EXTRA_SUBID = "subId";
- // {@link ComponentName} of the Service used to run tether provisioning.
- private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(
- Resources.getSystem().getString(config_wifi_tether_enable));
+ private final ComponentName mSilentProvisioningService;
private static final int MS_PER_HOUR = 60 * 60 * 1000;
private static final int EVENT_START_PROVISIONING = 0;
private static final int EVENT_STOP_PROVISIONING = 1;
@@ -122,6 +118,8 @@
mHandler = new EntitlementHandler(masterHandler.getLooper());
mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_PROVISIONING_ALARM),
null, mHandler);
+ mSilentProvisioningService = ComponentName.unflattenFromString(
+ mContext.getResources().getString(R.string.config_wifi_tether_enable));
}
public void setOnUiEntitlementFailedListener(final OnUiEntitlementFailedListener listener) {
@@ -377,7 +375,7 @@
intent.putExtra(EXTRA_RUN_PROVISION, true);
intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
intent.putExtra(EXTRA_SUBID, subId);
- intent.setComponent(TETHER_SERVICE);
+ intent.setComponent(mSilentProvisioningService);
// Only admin user can change tethering and SilentTetherProvisioning don't need to
// show UI, it is fine to always start setting's background service as system user.
mContext.startService(intent);
diff --git a/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
index ce7c2a6..cc36f4a 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
@@ -16,35 +16,40 @@
package com.android.server.connectivity.tethering;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.STATS_PER_UID;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
-import static android.net.TrafficStats.UID_TETHERING;
+import static android.net.NetworkStats.UID_TETHERING;
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.usage.NetworkStatsManager;
import android.content.ContentResolver;
-import android.net.ITetheringStatsProvider;
import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkStats;
+import android.net.NetworkStats.Entry;
import android.net.RouteInfo;
import android.net.netlink.ConntrackMessage;
import android.net.netlink.NetlinkConstants;
import android.net.netlink.NetlinkSocket;
+import android.net.netstats.provider.AbstractNetworkStatsProvider;
+import android.net.netstats.provider.NetworkStatsProviderCallback;
import android.net.util.SharedLog;
import android.os.Handler;
-import android.os.INetworkManagementService;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.SystemClock;
import android.provider.Settings;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.text.TextUtils;
+import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats;
@@ -73,13 +78,19 @@
private static final String ANYIP = "0.0.0.0";
private static final ForwardedStats EMPTY_STATS = new ForwardedStats();
+ @VisibleForTesting
+ enum StatsType {
+ STATS_PER_IFACE,
+ STATS_PER_UID,
+ }
+
private enum UpdateType { IF_NEEDED, FORCE };
private final Handler mHandler;
private final OffloadHardwareInterface mHwInterface;
private final ContentResolver mContentResolver;
- private final INetworkManagementService mNms;
- private final ITetheringStatsProvider mStatsProvider;
+ private final @NonNull OffloadTetheringStatsProvider mStatsProvider;
+ private final @Nullable NetworkStatsProviderCallback mStatsProviderCb;
private final SharedLog mLog;
private final HashMap<String, LinkProperties> mDownstreams;
private boolean mConfigInitialized;
@@ -109,22 +120,23 @@
private int mNatUpdateNetlinkErrors;
public OffloadController(Handler h, OffloadHardwareInterface hwi,
- ContentResolver contentResolver, INetworkManagementService nms, SharedLog log) {
+ ContentResolver contentResolver, NetworkStatsManager nsm, SharedLog log) {
mHandler = h;
mHwInterface = hwi;
mContentResolver = contentResolver;
- mNms = nms;
mStatsProvider = new OffloadTetheringStatsProvider();
mLog = log.forSubComponent(TAG);
mDownstreams = new HashMap<>();
mExemptPrefixes = new HashSet<>();
mLastLocalPrefixStrs = new HashSet<>();
-
+ NetworkStatsProviderCallback providerCallback = null;
try {
- mNms.registerTetheringStatsProvider(mStatsProvider, getClass().getSimpleName());
- } catch (RemoteException e) {
- mLog.e("Cannot register offload stats provider: " + e);
+ providerCallback = nsm.registerNetworkStatsProvider(
+ getClass().getSimpleName(), mStatsProvider);
+ } catch (RuntimeException e) {
+ Log.wtf(TAG, "Cannot register offload stats provider: " + e);
}
+ mStatsProviderCb = providerCallback;
}
/** Start hardware offload. */
@@ -173,7 +185,7 @@
// and we need to synchronize stats and limits between
// software and hardware forwarding.
updateStatsForAllUpstreams();
- forceTetherStatsPoll();
+ mStatsProvider.pushTetherStats();
}
@Override
@@ -186,7 +198,7 @@
// limits set take into account any software tethering
// traffic that has been happening in the meantime.
updateStatsForAllUpstreams();
- forceTetherStatsPoll();
+ mStatsProvider.pushTetherStats();
// [2] (Re)Push all state.
computeAndPushLocalPrefixes(UpdateType.FORCE);
pushAllDownstreamState();
@@ -204,14 +216,11 @@
// the HAL queued the callback.
// TODO: rev the HAL so that it provides an interface name.
- // Fetch current stats, so that when our notification reaches
- // NetworkStatsService and triggers a poll, we will respond with
- // current data (which will be above the limit that was reached).
- // Note that if we just changed upstream, this is unnecessary but harmless.
- // The stats for the previous upstream were already updated on this thread
- // just after the upstream was changed, so they are also up-to-date.
updateStatsForCurrentUpstream();
- forceTetherStatsPoll();
+ mStatsProvider.pushTetherStats();
+ // Push stats to service does not cause the service react to it immediately.
+ // Inform the service about limit reached.
+ if (mStatsProviderCb != null) mStatsProviderCb.onLimitReached();
}
@Override
@@ -253,42 +262,37 @@
return mConfigInitialized && mControlInitialized;
}
- private class OffloadTetheringStatsProvider extends ITetheringStatsProvider.Stub {
- @Override
- public NetworkStats getTetherStats(int how) {
- // getTetherStats() is the only function in OffloadController that can be called from
- // a different thread. Do not attempt to update stats by querying the offload HAL
- // synchronously from a different thread than our Handler thread. http://b/64771555.
- Runnable updateStats = () -> {
- updateStatsForCurrentUpstream();
- };
- if (Looper.myLooper() == mHandler.getLooper()) {
- updateStats.run();
- } else {
- mHandler.post(updateStats);
- }
+ @VisibleForTesting
+ class OffloadTetheringStatsProvider extends AbstractNetworkStatsProvider {
+ // These stats must only ever be touched on the handler thread.
+ @NonNull
+ private NetworkStats mIfaceStats = new NetworkStats(0L, 0);
+ @NonNull
+ private NetworkStats mUidStats = new NetworkStats(0L, 0);
- NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
- NetworkStats.Entry entry = new NetworkStats.Entry();
- entry.set = SET_DEFAULT;
- entry.tag = TAG_NONE;
- entry.uid = (how == STATS_PER_UID) ? UID_TETHERING : UID_ALL;
+ @VisibleForTesting
+ @NonNull
+ NetworkStats getTetherStats(@NonNull StatsType how) {
+ NetworkStats stats = new NetworkStats(0L, 0);
+ final int uid = (how == StatsType.STATS_PER_UID) ? UID_TETHERING : UID_ALL;
- for (Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) {
- ForwardedStats value = kv.getValue();
- entry.iface = kv.getKey();
- entry.rxBytes = value.rxBytes;
- entry.txBytes = value.txBytes;
- stats.addEntry(entry);
+ for (final Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) {
+ final ForwardedStats value = kv.getValue();
+ final Entry entry = new Entry(kv.getKey(), uid, SET_DEFAULT, TAG_NONE, METERED_NO,
+ ROAMING_NO, DEFAULT_NETWORK_NO, value.rxBytes, 0L, value.txBytes, 0L, 0L);
+ stats = stats.addValues(entry);
}
return stats;
}
@Override
- public void setInterfaceQuota(String iface, long quotaBytes) {
+ public void setLimit(String iface, long quotaBytes) {
+ mLog.i("setLimit: " + iface + "," + quotaBytes);
+ // Listen for all iface is necessary since upstream might be changed after limit
+ // is set.
mHandler.post(() -> {
- if (quotaBytes == ITetheringStatsProvider.QUOTA_UNLIMITED) {
+ if (quotaBytes == QUOTA_UNLIMITED) {
mInterfaceQuotas.remove(iface);
} else {
mInterfaceQuotas.put(iface, quotaBytes);
@@ -296,6 +300,42 @@
maybeUpdateDataLimit(iface);
});
}
+
+ /**
+ * Push stats to service, but does not cause a force polling. Note that this can only be
+ * called on the handler thread.
+ */
+ public void pushTetherStats() {
+ // TODO: remove the accumulated stats and report the diff from HAL directly.
+ if (null == mStatsProviderCb) return;
+ final NetworkStats ifaceDiff =
+ getTetherStats(StatsType.STATS_PER_IFACE).subtract(mIfaceStats);
+ final NetworkStats uidDiff =
+ getTetherStats(StatsType.STATS_PER_UID).subtract(mUidStats);
+ try {
+ mStatsProviderCb.onStatsUpdated(0 /* token */, ifaceDiff, uidDiff);
+ mIfaceStats = mIfaceStats.add(ifaceDiff);
+ mUidStats = mUidStats.add(uidDiff);
+ } catch (RuntimeException e) {
+ mLog.e("Cannot report network stats: ", e);
+ }
+ }
+
+ @Override
+ public void requestStatsUpdate(int token) {
+ mLog.i("requestStatsUpdate: " + token);
+ // Do not attempt to update stats by querying the offload HAL
+ // synchronously from a different thread than the Handler thread. http://b/64771555.
+ mHandler.post(() -> {
+ updateStatsForCurrentUpstream();
+ pushTetherStats();
+ });
+ }
+
+ @Override
+ public void setAlert(long quotaBytes) {
+ // TODO: Ask offload HAL to notify alert without stopping traffic.
+ }
}
private String currentUpstreamInterface() {
@@ -353,14 +393,6 @@
}
}
- private void forceTetherStatsPoll() {
- try {
- mNms.tetherLimitReached(mStatsProvider);
- } catch (RemoteException e) {
- mLog.e("Cannot report data limit reached: " + e);
- }
- }
-
/** Set current tethering upstream LinkProperties. */
public void setUpstreamLinkProperties(LinkProperties lp) {
if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return;
diff --git a/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
index 4a8ef1f..90b9d3f 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -29,6 +29,8 @@
import android.os.RemoteException;
import android.system.OsConstants;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.util.ArrayList;
@@ -91,6 +93,12 @@
txBytes = 0;
}
+ @VisibleForTesting
+ public ForwardedStats(long rxBytes, long txBytes) {
+ this.rxBytes = rxBytes;
+ this.txBytes = txBytes;
+ }
+
/** Add Tx/Rx bytes. */
public void add(ForwardedStats other) {
rxBytes += other.rxBytes;
diff --git a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index 038d7ae..2b27604 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -20,6 +20,7 @@
import static android.hardware.usb.UsbManager.USB_CONFIGURED;
import static android.hardware.usb.UsbManager.USB_CONNECTED;
import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
+import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
@@ -53,6 +54,7 @@
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.app.usage.NetworkStatsManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothProfile;
@@ -63,9 +65,8 @@
import android.content.IntentFilter;
import android.content.res.Resources;
import android.hardware.usb.UsbManager;
+import android.net.ConnectivityManager;
import android.net.INetd;
-import android.net.INetworkPolicyManager;
-import android.net.INetworkStatsService;
import android.net.ITetheringEventCallback;
import android.net.IpPrefix;
import android.net.LinkAddress;
@@ -176,8 +177,6 @@
private final Context mContext;
private final ArrayMap<String, TetherState> mTetherStates;
private final BroadcastReceiver mStateReceiver;
- private final INetworkStatsService mStatsService;
- private final INetworkPolicyManager mPolicyManager;
private final Looper mLooper;
private final StateMachine mTetherMasterSM;
private final OffloadController mOffloadController;
@@ -207,13 +206,12 @@
private boolean mWifiTetherRequested;
private Network mTetherUpstream;
private TetherStatesParcel mTetherStatesParcel;
+ private boolean mDataSaverEnabled = false;
public Tethering(TetheringDependencies deps) {
mLog.mark("Tethering.constructed");
mDeps = deps;
mContext = mDeps.getContext();
- mStatsService = mDeps.getINetworkStatsService();
- mPolicyManager = mDeps.getINetworkPolicyManager();
mNetd = mDeps.getINetd(mContext);
mLooper = mDeps.getTetheringLooper();
@@ -224,10 +222,12 @@
mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps);
mTetherMasterSM.start();
+ final NetworkStatsManager statsManager =
+ (NetworkStatsManager) mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
mHandler = mTetherMasterSM.getHandler();
mOffloadController = new OffloadController(mHandler,
mDeps.getOffloadHardwareInterface(mHandler, mLog), mContext.getContentResolver(),
- mDeps.getINetworkManagementService(), mLog);
+ statsManager, mLog);
mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
mForwardedDownstreams = new HashSet<>();
@@ -264,7 +264,7 @@
}
final UserManager userManager = (UserManager) mContext.getSystemService(
- Context.USER_SERVICE);
+ Context.USER_SERVICE);
mTetheringRestriction = new UserRestrictionActionListener(userManager, this);
final TetheringThreadExecutor executor = new TetheringThreadExecutor(mHandler);
mActiveDataSubIdListener = new ActiveDataSubIdListener(executor);
@@ -288,6 +288,7 @@
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
filter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
filter.addAction(UserManager.ACTION_USER_RESTRICTIONS_CHANGED);
+ filter.addAction(ACTION_RESTRICT_BACKGROUND_CHANGED);
mContext.registerReceiver(mStateReceiver, filter, null, handler);
}
@@ -484,7 +485,7 @@
}
private void setBluetoothTethering(final boolean enable, final ResultReceiver receiver) {
- final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ final BluetoothAdapter adapter = mDeps.getBluetoothAdapter();
if (adapter == null || !adapter.isEnabled()) {
Log.w(TAG, "Tried to enable bluetooth tethering with null or disabled adapter. null: "
+ (adapter == null));
@@ -775,6 +776,9 @@
} else if (action.equals(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)) {
mLog.log("OBSERVED user restrictions changed");
handleUserRestrictionAction();
+ } else if (action.equals(ACTION_RESTRICT_BACKGROUND_CHANGED)) {
+ mLog.log("OBSERVED data saver changed");
+ handleDataSaverChanged();
}
}
@@ -885,6 +889,20 @@
private void handleUserRestrictionAction() {
mTetheringRestriction.onUserRestrictionsChanged();
}
+
+ private void handleDataSaverChanged() {
+ final ConnectivityManager connMgr = (ConnectivityManager) mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ final boolean isDataSaverEnabled = connMgr.getRestrictBackgroundStatus()
+ != ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+
+ if (mDataSaverEnabled == isDataSaverEnabled) return;
+
+ mDataSaverEnabled = isDataSaverEnabled;
+ if (mDataSaverEnabled) {
+ untetherAll();
+ }
+ }
}
@VisibleForTesting
@@ -1982,15 +2000,6 @@
mLog.log(String.format("OBSERVED iface=%s state=%s error=%s", iface, state, error));
- try {
- // Notify that we're tethering (or not) this interface.
- // This is how data saver for instance knows if the user explicitly
- // turned on tethering (thus keeping us from being in data saver mode).
- mPolicyManager.onTetheringChanged(iface, state == IpServer.STATE_TETHERED);
- } catch (RemoteException e) {
- // Not really very much we can do here.
- }
-
// If TetherMasterSM is in ErrorState, TetherMasterSM stays there.
// Thus we give a chance for TetherMasterSM to recover to InitialState
// by sending CMD_CLEAR_ERROR
@@ -2054,7 +2063,7 @@
mLog.log("adding TetheringInterfaceStateMachine for: " + iface);
final TetherState tetherState = new TetherState(
- new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mStatsService,
+ new IpServer(iface, mLooper, interfaceType, mLog, mNetd,
makeControlCallback(), mConfig.enableLegacyDhcpServer,
mDeps.getIpServerDependencies()));
mTetherStates.put(iface, tetherState);
diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
index dbe7892..068c346 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -23,18 +23,6 @@
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
-import static com.android.internal.R.array.config_mobile_hotspot_provision_app;
-import static com.android.internal.R.array.config_tether_bluetooth_regexs;
-import static com.android.internal.R.array.config_tether_dhcp_range;
-import static com.android.internal.R.array.config_tether_upstream_types;
-import static com.android.internal.R.array.config_tether_usb_regexs;
-import static com.android.internal.R.array.config_tether_wifi_p2p_regexs;
-import static com.android.internal.R.array.config_tether_wifi_regexs;
-import static com.android.internal.R.bool.config_tether_upstream_automatic;
-import static com.android.internal.R.integer.config_mobile_hotspot_provision_check_period;
-import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui;
-import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
-
import android.content.Context;
import android.content.res.Resources;
import android.net.TetheringConfigurationParcel;
@@ -45,6 +33,7 @@
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.networkstack.tethering.R;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -113,27 +102,30 @@
activeDataSubId = id;
Resources res = getResources(ctx, activeDataSubId);
- tetherableUsbRegexs = getResourceStringArray(res, config_tether_usb_regexs);
+ tetherableUsbRegexs = getResourceStringArray(res, R.array.config_tether_usb_regexs);
// TODO: Evaluate deleting this altogether now that Wi-Fi always passes
// us an interface name. Careful consideration needs to be given to
// implications for Settings and for provisioning checks.
- tetherableWifiRegexs = getResourceStringArray(res, config_tether_wifi_regexs);
- tetherableWifiP2pRegexs = getResourceStringArray(res, config_tether_wifi_p2p_regexs);
- tetherableBluetoothRegexs = getResourceStringArray(res, config_tether_bluetooth_regexs);
+ tetherableWifiRegexs = getResourceStringArray(res, R.array.config_tether_wifi_regexs);
+ tetherableWifiP2pRegexs = getResourceStringArray(
+ res, R.array.config_tether_wifi_p2p_regexs);
+ tetherableBluetoothRegexs = getResourceStringArray(
+ res, R.array.config_tether_bluetooth_regexs);
isDunRequired = checkDunRequired(ctx);
- chooseUpstreamAutomatically = getResourceBoolean(res, config_tether_upstream_automatic);
+ chooseUpstreamAutomatically = getResourceBoolean(
+ res, R.bool.config_tether_upstream_automatic);
preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, isDunRequired);
legacyDhcpRanges = getLegacyDhcpRanges(res);
defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
enableLegacyDhcpServer = getEnableLegacyDhcpServer(res);
- provisioningApp = getResourceStringArray(res, config_mobile_hotspot_provision_app);
+ provisioningApp = getResourceStringArray(res, R.array.config_mobile_hotspot_provision_app);
provisioningAppNoUi = getProvisioningAppNoUi(res);
provisioningCheckPeriod = getResourceInteger(res,
- config_mobile_hotspot_provision_check_period,
+ R.integer.config_mobile_hotspot_provision_check_period,
0 /* No periodic re-check */);
configLog.log(toString());
@@ -248,7 +240,7 @@
}
private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) {
- final int[] ifaceTypes = res.getIntArray(config_tether_upstream_types);
+ final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types);
final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
for (int i : ifaceTypes) {
switch (i) {
@@ -298,7 +290,7 @@
}
private static String[] getLegacyDhcpRanges(Resources res) {
- final String[] fromResource = getResourceStringArray(res, config_tether_dhcp_range);
+ final String[] fromResource = getResourceStringArray(res, R.array.config_tether_dhcp_range);
if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) {
return fromResource;
}
@@ -307,7 +299,7 @@
private static String getProvisioningAppNoUi(Resources res) {
try {
- return res.getString(config_mobile_hotspot_provision_app_no_ui);
+ return res.getString(R.string.config_mobile_hotspot_provision_app_no_ui);
} catch (Resources.NotFoundException e) {
return "";
}
@@ -339,7 +331,7 @@
}
private boolean getEnableLegacyDhcpServer(final Resources res) {
- return getResourceBoolean(res, config_tether_enable_legacy_dhcp_server)
+ return getResourceBoolean(res, R.bool.config_tether_enable_legacy_dhcp_server)
|| getDeviceConfigBoolean(TETHER_ENABLE_LEGACY_DHCP_SERVER);
}
diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
index b16b329..247ed47 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -16,10 +16,9 @@
package com.android.server.connectivity.tethering;
+import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.net.INetd;
-import android.net.INetworkPolicyManager;
-import android.net.INetworkStatsService;
import android.net.NetworkRequest;
import android.net.ip.IpServer;
import android.net.util.SharedLog;
@@ -107,23 +106,6 @@
}
/**
- * Get a reference to INetworkStatsService to force update tethering usage.
- * Note: This should be removed in R development cycle.
- */
- public INetworkStatsService getINetworkStatsService() {
- return INetworkStatsService.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
- }
-
- /**
- * Get a reference to INetworkPolicyManager to be used by tethering.
- */
- public INetworkPolicyManager getINetworkPolicyManager() {
- return INetworkPolicyManager.Stub.asInterface(
- ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
- }
-
- /**
* Get a reference to INetd to be used by tethering.
*/
public INetd getINetd(Context context) {
@@ -140,4 +122,9 @@
* Get Context of TetheringSerice.
*/
public abstract Context getContext();
+
+ /**
+ * Get a reference to BluetoothAdapter to be used by tethering.
+ */
+ public abstract BluetoothAdapter getBluetoothAdapter();
}
diff --git a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
index e4e4a09..cb7d392 100644
--- a/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ b/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -24,6 +24,7 @@
import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR;
import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
import android.net.IIntResultListener;
@@ -42,7 +43,6 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.ResultReceiver;
-import android.os.ServiceManager;
import android.os.SystemProperties;
import android.os.UserManager;
import android.provider.Settings;
@@ -363,7 +363,7 @@
IBinder connector;
try {
final long before = System.currentTimeMillis();
- while ((connector = ServiceManager.getService(
+ while ((connector = (IBinder) mContext.getSystemService(
Context.NETWORK_STACK_SERVICE)) == null) {
if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
Log.wtf(TAG, "Timeout, fail to get INetworkStackConnector");
@@ -377,6 +377,11 @@
}
return INetworkStackConnector.Stub.asInterface(connector);
}
+
+ @Override
+ public BluetoothAdapter getBluetoothAdapter() {
+ return BluetoothAdapter.getDefaultAdapter();
+ }
};
}
return mDeps;
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index 65a0ac1..1f50b6b 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -52,7 +52,6 @@
import static org.mockito.Mockito.when;
import android.net.INetd;
-import android.net.INetworkStatsService;
import android.net.InterfaceConfigurationParcel;
import android.net.IpPrefix;
import android.net.LinkAddress;
@@ -99,7 +98,6 @@
private static final int MAKE_DHCPSERVER_TIMEOUT_MS = 1000;
@Mock private INetd mNetd;
- @Mock private INetworkStatsService mStatsService;
@Mock private IpServer.Callback mCallback;
@Mock private SharedLog mSharedLog;
@Mock private IDhcpServer mDhcpServer;
@@ -139,13 +137,13 @@
mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH;
}
mIpServer = new IpServer(
- IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mStatsService,
+ IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd,
mCallback, usingLegacyDhcp, mDependencies);
mIpServer.start();
// Starting the state machine always puts us in a consistent state and notifies
// the rest of the world that we've changed from an unknown to available state.
mLooper.dispatchAll();
- reset(mNetd, mStatsService, mCallback);
+ reset(mNetd, mCallback);
when(mRaDaemon.start()).thenReturn(true);
}
@@ -162,7 +160,7 @@
if (upstreamIface != null) {
dispatchTetherConnectionChanged(upstreamIface);
}
- reset(mNetd, mStatsService, mCallback);
+ reset(mNetd, mCallback);
}
@Before public void setUp() throws Exception {
@@ -173,13 +171,13 @@
@Test
public void startsOutAvailable() {
mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
- mNetd, mStatsService, mCallback, false /* usingLegacyDhcp */, mDependencies);
+ mNetd, mCallback, false /* usingLegacyDhcp */, mDependencies);
mIpServer.start();
mLooper.dispatchAll();
verify(mCallback).updateInterfaceState(
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
- verifyNoMoreInteractions(mCallback, mNetd, mStatsService);
+ verifyNoMoreInteractions(mCallback, mNetd);
}
@Test
@@ -198,7 +196,7 @@
// None of these commands should trigger us to request action from
// the rest of the system.
dispatchCommand(command);
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
}
@@ -210,7 +208,7 @@
verify(mCallback).updateInterfaceState(
mIpServer, STATE_UNAVAILABLE, TETHER_ERROR_NO_ERROR);
verify(mCallback).updateLinkProperties(eq(mIpServer), any(LinkProperties.class));
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
@Test
@@ -228,7 +226,7 @@
mIpServer, STATE_TETHERED, TETHER_ERROR_NO_ERROR);
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), any(LinkProperties.class));
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
@Test
@@ -236,7 +234,7 @@
initTetheredStateMachine(TETHERING_BLUETOOTH, null);
dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
- InOrder inOrder = inOrder(mNetd, mStatsService, mCallback);
+ InOrder inOrder = inOrder(mNetd, mCallback);
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
inOrder.verify(mNetd).tetherInterfaceRemove(IFACE_NAME);
inOrder.verify(mNetd).networkRemoveInterface(INetd.LOCAL_NET_ID, IFACE_NAME);
@@ -245,7 +243,7 @@
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), any(LinkProperties.class));
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
@Test
@@ -265,7 +263,7 @@
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), mLinkPropertiesCaptor.capture());
assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
@Test
@@ -285,7 +283,7 @@
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), mLinkPropertiesCaptor.capture());
assertIPv4AddressAndDirectlyConnectedRoute(mLinkPropertiesCaptor.getValue());
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
@Test
@@ -298,7 +296,7 @@
InOrder inOrder = inOrder(mNetd);
inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
@Test
@@ -306,13 +304,12 @@
initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
- InOrder inOrder = inOrder(mNetd, mStatsService);
- inOrder.verify(mStatsService).forceUpdate();
+ InOrder inOrder = inOrder(mNetd);
inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
@Test
@@ -322,12 +319,10 @@
doThrow(RemoteException.class).when(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
- InOrder inOrder = inOrder(mNetd, mStatsService);
- inOrder.verify(mStatsService).forceUpdate();
+ InOrder inOrder = inOrder(mNetd);
inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
- inOrder.verify(mStatsService).forceUpdate();
inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2);
}
@@ -340,13 +335,11 @@
IFACE_NAME, UPSTREAM_IFACE2);
dispatchTetherConnectionChanged(UPSTREAM_IFACE2);
- InOrder inOrder = inOrder(mNetd, mStatsService);
- inOrder.verify(mStatsService).forceUpdate();
+ InOrder inOrder = inOrder(mNetd);
inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).tetherAddForward(IFACE_NAME, UPSTREAM_IFACE2);
inOrder.verify(mNetd).ipfwdAddInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
- inOrder.verify(mStatsService).forceUpdate();
inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE2);
inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE2);
}
@@ -356,8 +349,7 @@
initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE);
dispatchCommand(IpServer.CMD_TETHER_UNREQUESTED);
- InOrder inOrder = inOrder(mNetd, mStatsService, mCallback);
- inOrder.verify(mStatsService).forceUpdate();
+ InOrder inOrder = inOrder(mNetd, mCallback);
inOrder.verify(mNetd).ipfwdRemoveInterfaceForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).tetherRemoveForward(IFACE_NAME, UPSTREAM_IFACE);
inOrder.verify(mNetd).tetherApplyDnsInterfaces();
@@ -368,7 +360,7 @@
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), any(LinkProperties.class));
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
@Test
@@ -435,11 +427,11 @@
public void ignoresDuplicateUpstreamNotifications() throws Exception {
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE);
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
for (int i = 0; i < 5; i++) {
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
- verifyNoMoreInteractions(mNetd, mStatsService, mCallback);
+ verifyNoMoreInteractions(mNetd, mCallback);
}
}
diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
index 79bba7f..4f07461 100644
--- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
+++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -27,7 +27,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -56,10 +55,10 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.R;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.internal.util.test.BroadcastInterceptingContext;
+import com.android.networkstack.tethering.R;
import org.junit.After;
import org.junit.Before;
@@ -157,16 +156,18 @@
eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
when(mResources.getStringArray(R.array.config_tether_dhcp_range))
- .thenReturn(new String[0]);
+ .thenReturn(new String[0]);
when(mResources.getStringArray(R.array.config_tether_usb_regexs))
- .thenReturn(new String[0]);
+ .thenReturn(new String[0]);
when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
- .thenReturn(new String[0]);
+ .thenReturn(new String[0]);
when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
- .thenReturn(new String[0]);
+ .thenReturn(new String[0]);
when(mResources.getIntArray(R.array.config_tether_upstream_types))
- .thenReturn(new int[0]);
- when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
+ .thenReturn(new int[0]);
+ when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+ false);
+ when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn("");
when(mLog.forSubComponent(anyString())).thenReturn(mLog);
mMockContext = new MockContext(mContext);
diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
index 7886ca6..7e62e5a 100644
--- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/OffloadControllerTest.java
@@ -16,21 +16,26 @@
package com.android.server.connectivity.tethering;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
import static android.net.NetworkStats.SET_DEFAULT;
-import static android.net.NetworkStats.STATS_PER_IFACE;
-import static android.net.NetworkStats.STATS_PER_UID;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
+import static android.net.NetworkStats.UID_TETHERING;
import static android.net.RouteInfo.RTN_UNICAST;
-import static android.net.TrafficStats.UID_TETHERING;
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
+import static com.android.server.connectivity.tethering.OffloadController.StatsType.STATS_PER_IFACE;
+import static com.android.server.connectivity.tethering.OffloadController.StatsType.STATS_PER_UID;
import static com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats;
import static com.android.testutils.MiscAssertsKt.assertContainsAll;
import static com.android.testutils.MiscAssertsKt.assertThrows;
+import static com.android.testutils.NetworkStatsUtilsKt.orderInsensitiveEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyObject;
@@ -39,11 +44,14 @@
import static org.mockito.Mockito.clearInvocations;
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.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.annotation.NonNull;
+import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.net.ITetheringStatsProvider;
@@ -51,10 +59,12 @@
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkStats;
+import android.net.NetworkStats.Entry;
import android.net.RouteInfo;
+import android.net.netstats.provider.AbstractNetworkStatsProvider;
+import android.net.netstats.provider.NetworkStatsProviderCallback;
import android.net.util.SharedLog;
import android.os.Handler;
-import android.os.INetworkManagementService;
import android.os.Looper;
import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
@@ -97,11 +107,13 @@
@Mock private OffloadHardwareInterface mHardware;
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
- @Mock private INetworkManagementService mNMService;
+ @Mock private NetworkStatsManager mStatsManager;
+ @Mock private NetworkStatsProviderCallback mTetherStatsProviderCb;
private final ArgumentCaptor<ArrayList> mStringArrayCaptor =
ArgumentCaptor.forClass(ArrayList.class);
- private final ArgumentCaptor<ITetheringStatsProvider.Stub> mTetherStatsProviderCaptor =
- ArgumentCaptor.forClass(ITetheringStatsProvider.Stub.class);
+ private final ArgumentCaptor<OffloadController.OffloadTetheringStatsProvider>
+ mTetherStatsProviderCaptor =
+ ArgumentCaptor.forClass(OffloadController.OffloadTetheringStatsProvider.class);
private final ArgumentCaptor<OffloadHardwareInterface.ControlCallback> mControlCallbackCaptor =
ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class);
private MockContentResolver mContentResolver;
@@ -114,6 +126,8 @@
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
when(mContext.getContentResolver()).thenReturn(mContentResolver);
FakeSettingsProvider.clearSettingsProvider();
+ when(mStatsManager.registerNetworkStatsProvider(anyString(), any()))
+ .thenReturn(mTetherStatsProviderCb);
}
@After public void tearDown() throws Exception {
@@ -139,9 +153,9 @@
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());
+ mHardware, mContentResolver, mStatsManager, new SharedLog("test"));
+ verify(mStatsManager).registerNetworkStatsProvider(anyString(),
+ mTetherStatsProviderCaptor.capture());
return offload;
}
@@ -384,12 +398,11 @@
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);
+ private static @NonNull Entry buildTestEntry(@NonNull OffloadController.StatsType how,
+ @NonNull String iface, long rxBytes, long txBytes) {
+ return new Entry(iface, how == STATS_PER_IFACE ? UID_ALL : UID_TETHERING, SET_DEFAULT,
+ TAG_NONE, METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes, 0L,
+ txBytes, 0L, 0L);
}
@Test
@@ -400,19 +413,16 @@
final OffloadController offload = makeOffloadController();
offload.start();
+ final OffloadController.OffloadTetheringStatsProvider provider =
+ mTetherStatsProviderCaptor.getValue();
+
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);
+ when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
+ new ForwardedStats(12345, 54321));
+ when(mHardware.getForwardedStats(eq(mobileIface))).thenReturn(
+ new ForwardedStats(999, 99999));
InOrder inOrder = inOrder(mHardware);
@@ -432,10 +442,35 @@
// Expect that we fetch stats from the previous upstream.
inOrder.verify(mHardware, times(1)).getForwardedStats(eq(mobileIface));
- ethernetStats = new ForwardedStats();
- ethernetStats.rxBytes = 100000;
- ethernetStats.txBytes = 100000;
- when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats);
+ // Verify that the fetched stats are stored.
+ final NetworkStats ifaceStats = provider.getTetherStats(STATS_PER_IFACE);
+ final NetworkStats uidStats = provider.getTetherStats(STATS_PER_UID);
+ final NetworkStats expectedIfaceStats = new NetworkStats(0L, 2)
+ .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
+ .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 12345, 54321));
+
+ final NetworkStats expectedUidStats = new NetworkStats(0L, 2)
+ .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
+ .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 12345, 54321));
+
+ assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStats));
+ assertTrue(orderInsensitiveEquals(expectedUidStats, uidStats));
+
+ final ArgumentCaptor<NetworkStats> ifaceStatsCaptor = ArgumentCaptor.forClass(
+ NetworkStats.class);
+ final ArgumentCaptor<NetworkStats> uidStatsCaptor = ArgumentCaptor.forClass(
+ NetworkStats.class);
+
+ // Force pushing stats update to verify the stats reported.
+ provider.pushTetherStats();
+ verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(),
+ ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
+ assertTrue(orderInsensitiveEquals(expectedIfaceStats, ifaceStatsCaptor.getValue()));
+ assertTrue(orderInsensitiveEquals(expectedUidStats, uidStatsCaptor.getValue()));
+
+
+ when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(
+ new ForwardedStats(100000, 100000));
offload.setUpstreamLinkProperties(null);
// Expect that we first clear the HAL's upstream parameters.
inOrder.verify(mHardware, times(1)).setUpstreamParameters(
@@ -443,37 +478,38 @@
// Expect that we fetch stats from the previous upstream.
inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface));
- ITetheringStatsProvider provider = mTetherStatsProviderCaptor.getValue();
- NetworkStats stats = provider.getTetherStats(STATS_PER_IFACE);
- NetworkStats perUidStats = provider.getTetherStats(STATS_PER_UID);
- waitForIdle();
// There is no current upstream, so no stats are fetched.
inOrder.verify(mHardware, never()).getForwardedStats(any());
inOrder.verifyNoMoreInteractions();
- assertEquals(2, stats.size());
- assertEquals(2, perUidStats.size());
+ // Verify that the stored stats is accumulated.
+ final NetworkStats ifaceStatsAccu = provider.getTetherStats(STATS_PER_IFACE);
+ final NetworkStats uidStatsAccu = provider.getTetherStats(STATS_PER_UID);
+ final NetworkStats expectedIfaceStatsAccu = new NetworkStats(0L, 2)
+ .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 999, 99999))
+ .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 112345, 154321));
- NetworkStats.Entry entry = null;
- for (int i = 0; i < stats.size(); i++) {
- assertEquals(UID_ALL, stats.getValues(i, entry).uid);
- assertEquals(UID_TETHERING, perUidStats.getValues(i, entry).uid);
- }
+ final NetworkStats expectedUidStatsAccu = new NetworkStats(0L, 2)
+ .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 999, 99999))
+ .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 112345, 154321));
- int ethernetPosition = ethernetIface.equals(stats.getValues(0, entry).iface) ? 0 : 1;
- int mobilePosition = 1 - ethernetPosition;
+ assertTrue(orderInsensitiveEquals(expectedIfaceStatsAccu, ifaceStatsAccu));
+ assertTrue(orderInsensitiveEquals(expectedUidStatsAccu, uidStatsAccu));
- entry = stats.getValues(mobilePosition, entry);
- assertNetworkStats(mobileIface, mobileStats, entry);
- entry = perUidStats.getValues(mobilePosition, entry);
- assertNetworkStats(mobileIface, mobileStats, entry);
+ // Verify that only diff of stats is reported.
+ reset(mTetherStatsProviderCb);
+ provider.pushTetherStats();
+ final NetworkStats expectedIfaceStatsDiff = new NetworkStats(0L, 2)
+ .addValues(buildTestEntry(STATS_PER_IFACE, mobileIface, 0, 0))
+ .addValues(buildTestEntry(STATS_PER_IFACE, ethernetIface, 100000, 100000));
- ethernetStats.rxBytes = 12345 + 100000;
- ethernetStats.txBytes = 54321 + 100000;
- entry = stats.getValues(ethernetPosition, entry);
- assertNetworkStats(ethernetIface, ethernetStats, entry);
- entry = perUidStats.getValues(ethernetPosition, entry);
- assertNetworkStats(ethernetIface, ethernetStats, entry);
+ final NetworkStats expectedUidStatsDiff = new NetworkStats(0L, 2)
+ .addValues(buildTestEntry(STATS_PER_UID, mobileIface, 0, 0))
+ .addValues(buildTestEntry(STATS_PER_UID, ethernetIface, 100000, 100000));
+ verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(),
+ ifaceStatsCaptor.capture(), uidStatsCaptor.capture());
+ assertTrue(orderInsensitiveEquals(expectedIfaceStatsDiff, ifaceStatsCaptor.getValue()));
+ assertTrue(orderInsensitiveEquals(expectedUidStatsDiff, uidStatsCaptor.getValue()));
}
@Test
@@ -493,19 +529,19 @@
lp.setInterfaceName(ethernetIface);
offload.setUpstreamLinkProperties(lp);
- ITetheringStatsProvider provider = mTetherStatsProviderCaptor.getValue();
+ AbstractNetworkStatsProvider provider = mTetherStatsProviderCaptor.getValue();
final InOrder inOrder = inOrder(mHardware);
when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true);
when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
// Applying an interface quota to the current upstream immediately sends it to the hardware.
- provider.setInterfaceQuota(ethernetIface, ethernetLimit);
+ provider.setLimit(ethernetIface, ethernetLimit);
waitForIdle();
inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit);
inOrder.verifyNoMoreInteractions();
// Applying an interface quota to another upstream does not take any immediate action.
- provider.setInterfaceQuota(mobileIface, mobileLimit);
+ provider.setLimit(mobileIface, mobileLimit);
waitForIdle();
inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
@@ -518,7 +554,7 @@
// Setting a limit of ITetheringStatsProvider.QUOTA_UNLIMITED causes the limit to be set
// to Long.MAX_VALUE.
- provider.setInterfaceQuota(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
+ provider.setLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
waitForIdle();
inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE);
@@ -526,7 +562,7 @@
when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false);
lp.setInterfaceName(ethernetIface);
offload.setUpstreamLinkProperties(lp);
- provider.setInterfaceQuota(mobileIface, mobileLimit);
+ provider.setLimit(mobileIface, mobileLimit);
waitForIdle();
inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
@@ -535,7 +571,7 @@
when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false);
lp.setInterfaceName(mobileIface);
offload.setUpstreamLinkProperties(lp);
- provider.setInterfaceQuota(mobileIface, mobileLimit);
+ provider.setLimit(mobileIface, mobileLimit);
waitForIdle();
inOrder.verify(mHardware).getForwardedStats(ethernetIface);
inOrder.verify(mHardware).stopOffloadControl();
@@ -551,7 +587,7 @@
OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
callback.onStoppedLimitReached();
- verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue());
+ verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
}
@Test
@@ -654,9 +690,10 @@
// Verify forwarded stats behaviour.
verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
+ // TODO: verify the exact stats reported.
+ verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
+ verifyNoMoreInteractions(mTetherStatsProviderCb);
verifyNoMoreInteractions(mHardware);
- verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue());
- verifyNoMoreInteractions(mNMService);
}
@Test
@@ -719,8 +756,8 @@
// Verify forwarded stats behaviour.
verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
- verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue());
- verifyNoMoreInteractions(mNMService);
+ verify(mTetherStatsProviderCb, times(1)).onStatsUpdated(anyInt(), any(), any());
+ verifyNoMoreInteractions(mTetherStatsProviderCb);
// TODO: verify local prefixes and downstreams are also pushed to the HAL.
verify(mHardware, times(1)).setLocalPrefixes(mStringArrayCaptor.capture());
diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
index ef97ad4..3635964 100644
--- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
+++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -26,13 +26,6 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.internal.R.array.config_mobile_hotspot_provision_app;
-import static com.android.internal.R.array.config_tether_bluetooth_regexs;
-import static com.android.internal.R.array.config_tether_dhcp_range;
-import static com.android.internal.R.array.config_tether_upstream_types;
-import static com.android.internal.R.array.config_tether_usb_regexs;
-import static com.android.internal.R.array.config_tether_wifi_regexs;
-import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -51,6 +44,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.test.BroadcastInterceptingContext;
+import com.android.networkstack.tethering.R;
import org.junit.After;
import org.junit.Before;
@@ -120,15 +114,18 @@
() -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
- when(mResources.getStringArray(config_tether_dhcp_range)).thenReturn(new String[0]);
- when(mResources.getStringArray(config_tether_usb_regexs)).thenReturn(new String[0]);
- when(mResources.getStringArray(config_tether_wifi_regexs))
+ when(mResources.getStringArray(R.array.config_tether_dhcp_range)).thenReturn(
+ new String[0]);
+ when(mResources.getStringArray(R.array.config_tether_usb_regexs)).thenReturn(new String[0]);
+ when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
.thenReturn(new String[]{ "test_wlan\\d" });
- when(mResources.getStringArray(config_tether_bluetooth_regexs)).thenReturn(new String[0]);
- when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[0]);
- when(mResources.getStringArray(config_mobile_hotspot_provision_app))
+ when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs)).thenReturn(
+ new String[0]);
+ when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]);
+ when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
.thenReturn(new String[0]);
- when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
+ when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+ false);
mHasTelephonyManager = true;
mMockContext = new MockContext(mContext);
mEnableLegacyDhcpServer = false;
@@ -140,7 +137,7 @@
}
private TetheringConfiguration getTetheringConfiguration(int... legacyTetherUpstreamTypes) {
- when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(
+ when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(
legacyTetherUpstreamTypes);
return new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
}
@@ -224,7 +221,7 @@
@Test
public void testNoDefinedUpstreamTypesAddsEthernet() {
- when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(new int[]{});
+ when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[]{});
when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
final TetheringConfiguration cfg = new TetheringConfiguration(
@@ -246,7 +243,7 @@
@Test
public void testDefinedUpstreamTypesSansEthernetAddsEthernet() {
- when(mResources.getIntArray(config_tether_upstream_types)).thenReturn(
+ when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(
new int[]{TYPE_WIFI, TYPE_MOBILE_HIPRI});
when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
@@ -264,7 +261,7 @@
@Test
public void testDefinedUpstreamTypesWithEthernetDoesNotAddEthernet() {
- when(mResources.getIntArray(config_tether_upstream_types))
+ when(mResources.getIntArray(R.array.config_tether_upstream_types))
.thenReturn(new int[]{TYPE_WIFI, TYPE_ETHERNET, TYPE_MOBILE_HIPRI});
when(mTelephonyManager.isTetheringApnRequired()).thenReturn(false);
@@ -282,7 +279,8 @@
@Test
public void testNewDhcpServerDisabled() {
- when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(true);
+ when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+ true);
doReturn(false).when(
() -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
@@ -291,7 +289,8 @@
new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
assertTrue(enableByRes.enableLegacyDhcpServer);
- when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
+ when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+ false);
doReturn(true).when(
() -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
@@ -303,7 +302,8 @@
@Test
public void testNewDhcpServerEnabled() {
- when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
+ when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+ false);
doReturn(false).when(
() -> DeviceConfig.getBoolean(eq(NAMESPACE_CONNECTIVITY),
eq(TetheringConfiguration.TETHER_ENABLE_LEGACY_DHCP_SERVER), anyBoolean()));
@@ -329,16 +329,17 @@
private void setUpResourceForSubId() {
when(mResourcesForSubId.getStringArray(
- config_tether_dhcp_range)).thenReturn(new String[0]);
+ R.array.config_tether_dhcp_range)).thenReturn(new String[0]);
when(mResourcesForSubId.getStringArray(
- config_tether_usb_regexs)).thenReturn(new String[0]);
+ R.array.config_tether_usb_regexs)).thenReturn(new String[0]);
when(mResourcesForSubId.getStringArray(
- config_tether_wifi_regexs)).thenReturn(new String[]{ "test_wlan\\d" });
+ R.array.config_tether_wifi_regexs)).thenReturn(new String[]{ "test_wlan\\d" });
when(mResourcesForSubId.getStringArray(
- config_tether_bluetooth_regexs)).thenReturn(new String[0]);
- when(mResourcesForSubId.getIntArray(config_tether_upstream_types)).thenReturn(new int[0]);
+ R.array.config_tether_bluetooth_regexs)).thenReturn(new String[0]);
+ when(mResourcesForSubId.getIntArray(R.array.config_tether_upstream_types)).thenReturn(
+ new int[0]);
when(mResourcesForSubId.getStringArray(
- config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME);
+ R.array.config_mobile_hotspot_provision_app)).thenReturn(PROVISIONING_APP_NAME);
}
}
diff --git a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index e1fe3bf..19470b4 100644
--- a/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -19,6 +19,9 @@
import static android.hardware.usb.UsbManager.USB_CONFIGURED;
import static android.hardware.usb.UsbManager.USB_CONNECTED;
import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
+import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
@@ -37,8 +40,6 @@
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-import static com.android.networkstack.tethering.R.bool.config_tether_enable_legacy_dhcp_server;
-
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -53,6 +54,7 @@
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
@@ -60,6 +62,8 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
+import android.app.usage.NetworkStatsManager;
+import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -68,9 +72,9 @@
import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
import android.hardware.usb.UsbManager;
+import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.INetworkPolicyManager;
-import android.net.INetworkStatsService;
import android.net.ITetheringEventCallback;
import android.net.InetAddresses;
import android.net.InterfaceConfigurationParcel;
@@ -120,6 +124,7 @@
import com.android.internal.util.StateMachine;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.networkstack.tethering.R;
import org.junit.After;
import org.junit.Before;
@@ -133,6 +138,7 @@
import java.net.Inet6Address;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Vector;
@RunWith(AndroidJUnit4.class)
@@ -152,7 +158,7 @@
@Mock private ApplicationInfo mApplicationInfo;
@Mock private Context mContext;
@Mock private INetworkManagementService mNMService;
- @Mock private INetworkStatsService mStatsService;
+ @Mock private NetworkStatsManager mStatsManager;
@Mock private INetworkPolicyManager mPolicyManager;
@Mock private OffloadHardwareInterface mOffloadHardwareInterface;
@Mock private Resources mResources;
@@ -167,6 +173,7 @@
@Mock private INetd mNetd;
@Mock private UserManager mUserManager;
@Mock private NetworkRequest mNetworkRequest;
+ @Mock private ConnectivityManager mCm;
private final MockIpServerDependencies mIpServerDependencies =
spy(new MockIpServerDependencies());
@@ -217,6 +224,8 @@
if (Context.USB_SERVICE.equals(name)) return mUsbManager;
if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
if (Context.USER_SERVICE.equals(name)) return mUserManager;
+ if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager;
+ if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
return super.getSystemService(name);
}
@@ -339,16 +348,6 @@
}
@Override
- public INetworkStatsService getINetworkStatsService() {
- return mStatsService;
- }
-
- @Override
- public INetworkPolicyManager getINetworkPolicyManager() {
- return mPolicyManager;
- }
-
- @Override
public INetd getINetd(Context context) {
return mNetd;
}
@@ -362,6 +361,12 @@
public Context getContext() {
return mServiceContext;
}
+
+ @Override
+ public BluetoothAdapter getBluetoothAdapter() {
+ // TODO: add test for bluetooth tethering.
+ return null;
+ }
}
private static UpstreamNetworkState buildMobileUpstreamState(boolean withIPv4,
@@ -420,24 +425,24 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range))
+ when(mResources.getStringArray(R.array.config_tether_dhcp_range))
.thenReturn(new String[0]);
- when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs))
+ when(mResources.getStringArray(R.array.config_tether_usb_regexs))
.thenReturn(new String[] { "test_rndis\\d" });
- when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs))
+ when(mResources.getStringArray(R.array.config_tether_wifi_regexs))
.thenReturn(new String[]{ "test_wlan\\d" });
- when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_p2p_regexs))
+ when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
.thenReturn(new String[]{ "test_p2p-p2p\\d-.*" });
- when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs))
+ when(mResources.getStringArray(R.array.config_tether_bluetooth_regexs))
.thenReturn(new String[0]);
- when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
- .thenReturn(new int[0]);
- when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic))
- .thenReturn(false);
- when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(false);
+ when(mResources.getIntArray(R.array.config_tether_upstream_types)).thenReturn(new int[0]);
+ when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(false);
+ when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+ false);
when(mNetd.interfaceGetList())
.thenReturn(new String[] {
TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME, TEST_P2P_IFNAME});
+ when(mResources.getString(R.string.config_wifi_tether_enable)).thenReturn("");
mInterfaceConfiguration = new InterfaceConfigurationParcel();
mInterfaceConfiguration.flags = new String[0];
when(mRouterAdvertisementDaemon.start())
@@ -457,7 +462,7 @@
mServiceContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(ACTION_TETHER_STATE_CHANGED));
mTethering = makeTethering();
- verify(mNMService).registerTetheringStatsProvider(any(), anyString());
+ verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any());
verify(mNetd).registerUnsolicitedEventListener(any());
final ArgumentCaptor<PhoneStateListener> phoneListenerCaptor =
ArgumentCaptor.forClass(PhoneStateListener.class);
@@ -501,13 +506,15 @@
p2pInfo.groupFormed = isGroupFormed;
p2pInfo.isGroupOwner = isGroupOwner;
- WifiP2pGroup group = new WifiP2pGroup();
- group.setIsGroupOwner(isGroupOwner);
- group.setInterface(ifname);
+ WifiP2pGroup group = mock(WifiP2pGroup.class);
+ when(group.isGroupOwner()).thenReturn(isGroupOwner);
+ when(group.getInterface()).thenReturn(ifname);
- final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
- intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO, p2pInfo);
- intent.putExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP, group);
+ final Intent intent = mock(Intent.class);
+ when(intent.getAction()).thenReturn(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
+ when(intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_INFO)).thenReturn(p2pInfo);
+ when(intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_GROUP)).thenReturn(group);
+
mServiceContext.sendBroadcastAsUserMultiplePermissions(intent, UserHandle.ALL,
P2P_RECEIVER_PERMISSIONS_FOR_BROADCAST);
}
@@ -698,7 +705,8 @@
@Test
public void workingMobileUsbTethering_IPv4LegacyDhcp() {
- when(mResources.getBoolean(config_tether_enable_legacy_dhcp_server)).thenReturn(true);
+ when(mResources.getBoolean(R.bool.config_tether_enable_legacy_dhcp_server)).thenReturn(
+ true);
sendConfigurationChanged();
final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
runUsbTethering(upstreamState);
@@ -786,8 +794,7 @@
@Test
public void configTetherUpstreamAutomaticIgnoresConfigTetherUpstreamTypes() throws Exception {
- when(mResources.getBoolean(com.android.internal.R.bool.config_tether_upstream_automatic))
- .thenReturn(true);
+ when(mResources.getBoolean(R.bool.config_tether_upstream_automatic)).thenReturn(true);
sendConfigurationChanged();
// Setup IPv6
@@ -1315,7 +1322,7 @@
private void workingWifiP2pGroupOwnerLegacyMode(
boolean emulateInterfaceStatusChanged) throws Exception {
// change to legacy mode and update tethering information by chaning SIM
- when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_p2p_regexs))
+ when(mResources.getStringArray(R.array.config_tether_wifi_p2p_regexs))
.thenReturn(new String[]{});
final int fakeSubId = 1234;
mPhoneStateListener.onActiveDataSubscriptionIdChanged(fakeSubId);
@@ -1353,6 +1360,50 @@
workingWifiP2pGroupClient(false);
}
+ private void setDataSaverEnabled(boolean enabled) {
+ final Intent intent = new Intent(ACTION_RESTRICT_BACKGROUND_CHANGED);
+ mServiceContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+
+ final int status = enabled ? RESTRICT_BACKGROUND_STATUS_ENABLED
+ : RESTRICT_BACKGROUND_STATUS_DISABLED;
+ when(mCm.getRestrictBackgroundStatus()).thenReturn(status);
+ mLooper.dispatchAll();
+ }
+
+ @Test
+ public void testDataSaverChanged() {
+ // Start Tethering.
+ final UpstreamNetworkState upstreamState = buildMobileIPv4UpstreamState();
+ runUsbTethering(upstreamState);
+ assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
+ // Data saver is ON.
+ setDataSaverEnabled(true);
+ // Verify that tethering should be disabled.
+ verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+ mTethering.interfaceRemoved(TEST_USB_IFNAME);
+ mLooper.dispatchAll();
+ assertEquals(mTethering.getTetheredIfaces(), new String[0]);
+ reset(mUsbManager);
+
+ runUsbTethering(upstreamState);
+ // Verify that user can start tethering again without turning OFF data saver.
+ assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
+
+ // If data saver is keep ON with change event, tethering should not be OFF this time.
+ setDataSaverEnabled(true);
+ verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+ assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
+
+ // If data saver is turned OFF, it should not change tethering.
+ setDataSaverEnabled(false);
+ verify(mUsbManager, times(0)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
+ assertContains(Arrays.asList(mTethering.getTetheredIfaces()), TEST_USB_IFNAME);
+ }
+
+ private static <T> void assertContains(Collection<T> collection, T element) {
+ assertTrue(element + " not found in " + collection, collection.contains(element));
+ }
+
// TODO: Test that a request for hotspot mode doesn't interfere with an
// already operating tethering mode interface.
}