Merge "Fix MtsTetheringTest fail to access hidden tethering api problem" into sc-dev
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index 89f1505..b1ffbb1 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -30,6 +30,7 @@
":services-tethering-shared-srcs",
],
static_libs: [
+ "NetworkStackApiStableShims",
"androidx.annotation_annotation",
"modules-utils-build",
"netlink-client",
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index add4f37..2eed968 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -47,7 +47,6 @@
import android.net.util.InterfaceParams;
import android.net.util.SharedLog;
import android.net.util.TetheringUtils.ForwardedStats;
-import android.os.ConditionVariable;
import android.os.Handler;
import android.system.ErrnoException;
import android.text.TextUtils;
@@ -735,43 +734,35 @@
* be allowed to be accessed on the handler thread.
*/
public void dump(@NonNull IndentingPrintWriter pw) {
- final ConditionVariable dumpDone = new ConditionVariable();
- mHandler.post(() -> {
- pw.println("mIsBpfEnabled: " + mIsBpfEnabled);
- pw.println("Polling " + (mPollingStarted ? "started" : "not started"));
- pw.println("Stats provider " + (mStatsProvider != null
- ? "registered" : "not registered"));
- pw.println("Upstream quota: " + mInterfaceQuotas.toString());
- pw.println("Polling interval: " + getPollingInterval() + " ms");
- pw.println("Bpf shim: " + mBpfCoordinatorShim.toString());
+ pw.println("mIsBpfEnabled: " + mIsBpfEnabled);
+ pw.println("Polling " + (mPollingStarted ? "started" : "not started"));
+ pw.println("Stats provider " + (mStatsProvider != null
+ ? "registered" : "not registered"));
+ pw.println("Upstream quota: " + mInterfaceQuotas.toString());
+ pw.println("Polling interval: " + getPollingInterval() + " ms");
+ pw.println("Bpf shim: " + mBpfCoordinatorShim.toString());
- pw.println("Forwarding stats:");
- pw.increaseIndent();
- if (mStats.size() == 0) {
- pw.println("<empty>");
- } else {
- dumpStats(pw);
- }
- pw.decreaseIndent();
-
- pw.println("Forwarding rules:");
- pw.increaseIndent();
- dumpIpv6UpstreamRules(pw);
- dumpIpv6ForwardingRules(pw);
- dumpIpv4ForwardingRules(pw);
- pw.decreaseIndent();
-
- pw.println();
- pw.println("Forwarding counters:");
- pw.increaseIndent();
- dumpCounters(pw);
- pw.decreaseIndent();
-
- dumpDone.open();
- });
- if (!dumpDone.block(DUMP_TIMEOUT_MS)) {
- pw.println("... dump timed-out after " + DUMP_TIMEOUT_MS + "ms");
+ pw.println("Forwarding stats:");
+ pw.increaseIndent();
+ if (mStats.size() == 0) {
+ pw.println("<empty>");
+ } else {
+ dumpStats(pw);
}
+ pw.decreaseIndent();
+
+ pw.println("Forwarding rules:");
+ pw.increaseIndent();
+ dumpIpv6UpstreamRules(pw);
+ dumpIpv6ForwardingRules(pw);
+ dumpIpv4ForwardingRules(pw);
+ pw.decreaseIndent();
+
+ pw.println();
+ pw.println("Forwarding counters:");
+ pw.increaseIndent();
+ dumpCounters(pw);
+ pw.decreaseIndent();
}
private void dumpStats(@NonNull IndentingPrintWriter pw) {
diff --git a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
index b1e3cfe..60fcfd0 100644
--- a/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
+++ b/Tethering/src/com/android/networkstack/tethering/EntitlementManager.java
@@ -41,7 +41,6 @@
import android.content.IntentFilter;
import android.net.util.SharedLog;
import android.os.Bundle;
-import android.os.ConditionVariable;
import android.os.Handler;
import android.os.Parcel;
import android.os.PersistableBundle;
@@ -516,25 +515,18 @@
* @param pw {@link PrintWriter} is used to print formatted
*/
public void dump(PrintWriter pw) {
- final ConditionVariable mWaiting = new ConditionVariable();
- mHandler.post(() -> {
- pw.print("isCellularUpstreamPermitted: ");
- pw.println(isCellularUpstreamPermitted());
- for (int type = mCurrentDownstreams.nextSetBit(0); type >= 0;
- type = mCurrentDownstreams.nextSetBit(type + 1)) {
- pw.print("Type: ");
- pw.print(typeString(type));
- if (mCurrentEntitlementResults.indexOfKey(type) > -1) {
- pw.print(", Value: ");
- pw.println(errorString(mCurrentEntitlementResults.get(type)));
- } else {
- pw.println(", Value: empty");
- }
+ pw.print("isCellularUpstreamPermitted: ");
+ pw.println(isCellularUpstreamPermitted());
+ for (int type = mCurrentDownstreams.nextSetBit(0); type >= 0;
+ type = mCurrentDownstreams.nextSetBit(type + 1)) {
+ pw.print("Type: ");
+ pw.print(typeString(type));
+ if (mCurrentEntitlementResults.indexOfKey(type) > -1) {
+ pw.print(", Value: ");
+ pw.println(errorString(mCurrentEntitlementResults.get(type)));
+ } else {
+ pw.println(", Value: empty");
}
- mWaiting.open();
- });
- if (!mWaiting.block(DUMP_TIMEOUT)) {
- pw.println("... dump timed out after " + DUMP_TIMEOUT + "ms");
}
pw.print("Exempted: [");
for (int type = mExemptedDownstreams.nextSetBit(0); type >= 0;
diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadController.java b/Tethering/src/com/android/networkstack/tethering/OffloadController.java
index 44e3916..beb1821 100644
--- a/Tethering/src/com/android/networkstack/tethering/OffloadController.java
+++ b/Tethering/src/com/android/networkstack/tethering/OffloadController.java
@@ -26,6 +26,8 @@
import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED;
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_1;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_NONE;
import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
@@ -114,11 +116,42 @@
private ConcurrentHashMap<String, ForwardedStats> mForwardedStats =
new ConcurrentHashMap<>(16, 0.75F, 1);
+ private static class InterfaceQuota {
+ public final long warningBytes;
+ public final long limitBytes;
+
+ public static InterfaceQuota MAX_VALUE = new InterfaceQuota(Long.MAX_VALUE, Long.MAX_VALUE);
+
+ InterfaceQuota(long warningBytes, long limitBytes) {
+ this.warningBytes = warningBytes;
+ this.limitBytes = limitBytes;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof InterfaceQuota)) return false;
+ InterfaceQuota that = (InterfaceQuota) o;
+ return warningBytes == that.warningBytes
+ && limitBytes == that.limitBytes;
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) (warningBytes * 3 + limitBytes * 5);
+ }
+
+ @Override
+ public String toString() {
+ return "InterfaceQuota{" + "warning=" + warningBytes + ", limit=" + limitBytes + '}';
+ }
+ }
+
// Maps upstream interface names to interface quotas.
// Always contains the latest value received from the framework for each interface, regardless
// of whether offload is currently running (or is even supported) on that interface. Only
// includes upstream interfaces that have a quota set.
- private HashMap<String, Long> mInterfaceQuotas = new HashMap<>();
+ private HashMap<String, InterfaceQuota> mInterfaceQuotas = new HashMap<>();
// Tracking remaining alert quota. Unlike limit quota is subject to interface, the alert
// quota is interface independent and global for tether offload. Note that this is only
@@ -250,6 +283,18 @@
}
@Override
+ public void onWarningReached() {
+ if (!started()) return;
+ mLog.log("onWarningReached");
+
+ updateStatsForCurrentUpstream();
+ if (mStatsProvider != null) {
+ mStatsProvider.pushTetherStats();
+ mStatsProvider.notifyWarningReached();
+ }
+ }
+
+ @Override
public void onNatTimeoutUpdate(int proto,
String srcAddr, int srcPort,
String dstAddr, int dstPort) {
@@ -263,7 +308,8 @@
mLog.i("tethering offload control not supported");
stop();
} else {
- mLog.log("tethering offload started");
+ mLog.log("tethering offload started, version: "
+ + OffloadHardwareInterface.halVerToString(mControlHalVersion));
mNatUpdateCallbacksReceived = 0;
mNatUpdateNetlinkErrors = 0;
maybeSchedulePollingStats();
@@ -322,24 +368,35 @@
@Override
public void onSetLimit(String iface, long quotaBytes) {
+ onSetWarningAndLimit(iface, QUOTA_UNLIMITED, quotaBytes);
+ }
+
+ @Override
+ public void onSetWarningAndLimit(@NonNull String iface,
+ long warningBytes, long limitBytes) {
// Listen for all iface is necessary since upstream might be changed after limit
// is set.
mHandler.post(() -> {
- final Long curIfaceQuota = mInterfaceQuotas.get(iface);
+ final InterfaceQuota curIfaceQuota = mInterfaceQuotas.get(iface);
+ final InterfaceQuota newIfaceQuota = new InterfaceQuota(
+ warningBytes == QUOTA_UNLIMITED ? Long.MAX_VALUE : warningBytes,
+ limitBytes == QUOTA_UNLIMITED ? Long.MAX_VALUE : limitBytes);
// If the quota is set to unlimited, the value set to HAL is Long.MAX_VALUE,
// which is ~8.4 x 10^6 TiB, no one can actually reach it. Thus, it is not
// useful to set it multiple times.
// Otherwise, the quota needs to be updated to tell HAL to re-count from now even
// if the quota is the same as the existing one.
- if (null == curIfaceQuota && QUOTA_UNLIMITED == quotaBytes) return;
+ if (null == curIfaceQuota && InterfaceQuota.MAX_VALUE.equals(newIfaceQuota)) {
+ return;
+ }
- if (quotaBytes == QUOTA_UNLIMITED) {
+ if (InterfaceQuota.MAX_VALUE.equals(newIfaceQuota)) {
mInterfaceQuotas.remove(iface);
} else {
- mInterfaceQuotas.put(iface, quotaBytes);
+ mInterfaceQuotas.put(iface, newIfaceQuota);
}
- maybeUpdateDataLimit(iface);
+ maybeUpdateDataWarningAndLimit(iface);
});
}
@@ -374,7 +431,11 @@
@Override
public void onSetAlert(long quotaBytes) {
- // TODO: Ask offload HAL to notify alert without stopping traffic.
+ // Ignore set alert calls from HAL V1.1 since the hardware supports set warning now.
+ // Thus, the software polling mechanism is not needed.
+ if (!useStatsPolling()) {
+ return;
+ }
// Post it to handler thread since it access remaining quota bytes.
mHandler.post(() -> {
updateAlertQuota(quotaBytes);
@@ -459,24 +520,32 @@
private boolean isPollingStatsNeeded() {
return started() && mRemainingAlertQuota > 0
+ && useStatsPolling()
&& !TextUtils.isEmpty(currentUpstreamInterface())
&& mDeps.getTetherConfig() != null
&& mDeps.getTetherConfig().getOffloadPollInterval()
>= DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS;
}
- private boolean maybeUpdateDataLimit(String iface) {
- // setDataLimit may only be called while offload is occurring on this upstream.
+ private boolean useStatsPolling() {
+ return mControlHalVersion == OFFLOAD_HAL_VERSION_1_0;
+ }
+
+ private boolean maybeUpdateDataWarningAndLimit(String iface) {
+ // setDataLimit or setDataWarningAndLimit may only be called while offload is occurring
+ // on this upstream.
if (!started() || !TextUtils.equals(iface, currentUpstreamInterface())) {
return true;
}
- Long limit = mInterfaceQuotas.get(iface);
- if (limit == null) {
- limit = Long.MAX_VALUE;
+ final InterfaceQuota quota = mInterfaceQuotas.getOrDefault(iface, InterfaceQuota.MAX_VALUE);
+ final boolean ret;
+ if (mControlHalVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ ret = mHwInterface.setDataWarningAndLimit(iface, quota.warningBytes, quota.limitBytes);
+ } else {
+ ret = mHwInterface.setDataLimit(iface, quota.limitBytes);
}
-
- return mHwInterface.setDataLimit(iface, limit);
+ return ret;
}
private void updateStatsForCurrentUpstream() {
@@ -630,7 +699,7 @@
maybeUpdateStats(prevUpstream);
// Data limits can only be set once offload is running on the upstream.
- success = maybeUpdateDataLimit(iface);
+ success = maybeUpdateDataWarningAndLimit(iface);
if (!success) {
// If we failed to set a data limit, don't use this upstream, because we don't want to
// blow through the data limit that we were told to apply.
diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
index 7685847..e3ac660 100644
--- a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
+++ b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java
@@ -24,10 +24,10 @@
import android.annotation.NonNull;
import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
-import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
+import android.hardware.tetheroffload.control.V1_1.ITetheringOffloadCallback;
import android.net.netlink.NetlinkSocket;
import android.net.netlink.StructNfGenMsg;
import android.net.netlink.StructNlMsgHdr;
@@ -39,6 +39,7 @@
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
+import android.util.Log;
import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
@@ -140,6 +141,8 @@
public void onSupportAvailable() {}
/** Offload stopped because of usage limit reached. */
public void onStoppedLimitReached() {}
+ /** Indicate that data warning quota is reached. */
+ public void onWarningReached() {}
/** Indicate to update NAT timeout. */
public void onNatTimeoutUpdate(int proto,
@@ -381,7 +384,8 @@
(controlCb == null) ? "null"
: "0x" + Integer.toHexString(System.identityHashCode(controlCb)));
- mTetheringOffloadCallback = new TetheringOffloadCallback(mHandler, mControlCallback, mLog);
+ mTetheringOffloadCallback = new TetheringOffloadCallback(
+ mHandler, mControlCallback, mLog, mOffloadControlVersion);
final CbResults results = new CbResults();
try {
mOffloadControl.initOffload(
@@ -480,6 +484,33 @@
return results.mSuccess;
}
+ /** Set data warning and limit value to offload management process. */
+ public boolean setDataWarningAndLimit(String iface, long warning, long limit) {
+ if (mOffloadControlVersion < OFFLOAD_HAL_VERSION_1_1) {
+ throw new IllegalArgumentException(
+ "setDataWarningAndLimit is not supported below HAL V1.1");
+ }
+ final String logmsg =
+ String.format("setDataWarningAndLimit(%s, %d, %d)", iface, warning, limit);
+
+ final CbResults results = new CbResults();
+ try {
+ ((android.hardware.tetheroffload.control.V1_1.IOffloadControl) mOffloadControl)
+ .setDataWarningAndLimit(
+ iface, warning, limit,
+ (boolean success, String errMsg) -> {
+ results.mSuccess = success;
+ results.mErrMsg = errMsg;
+ });
+ } catch (RemoteException e) {
+ record(logmsg, e);
+ return false;
+ }
+
+ record(logmsg, results);
+ return results.mSuccess;
+ }
+
/** Set upstream parameters to offload management process. */
public boolean setUpstreamParameters(
String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) {
@@ -565,35 +596,64 @@
public final Handler handler;
public final ControlCallback controlCb;
public final SharedLog log;
+ private final int mOffloadControlVersion;
- TetheringOffloadCallback(Handler h, ControlCallback cb, SharedLog sharedLog) {
+ TetheringOffloadCallback(
+ Handler h, ControlCallback cb, SharedLog sharedLog, int offloadControlVersion) {
handler = h;
controlCb = cb;
log = sharedLog;
+ this.mOffloadControlVersion = offloadControlVersion;
+ }
+
+ private void handleOnEvent(int event) {
+ switch (event) {
+ case OffloadCallbackEvent.OFFLOAD_STARTED:
+ controlCb.onStarted();
+ break;
+ case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR:
+ controlCb.onStoppedError();
+ break;
+ case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED:
+ controlCb.onStoppedUnsupported();
+ break;
+ case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE:
+ controlCb.onSupportAvailable();
+ break;
+ case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED:
+ controlCb.onStoppedLimitReached();
+ break;
+ case android.hardware.tetheroffload.control
+ .V1_1.OffloadCallbackEvent.OFFLOAD_WARNING_REACHED:
+ controlCb.onWarningReached();
+ break;
+ default:
+ log.e("Unsupported OffloadCallbackEvent: " + event);
+ }
}
@Override
public void onEvent(int event) {
+ // The implementation should never call onEvent()) if the event is already reported
+ // through newer callback.
+ if (mOffloadControlVersion > OFFLOAD_HAL_VERSION_1_0) {
+ Log.wtf(TAG, "onEvent(" + event + ") fired on HAL "
+ + halVerToString(mOffloadControlVersion));
+ }
handler.post(() -> {
- switch (event) {
- case OffloadCallbackEvent.OFFLOAD_STARTED:
- controlCb.onStarted();
- break;
- case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR:
- controlCb.onStoppedError();
- break;
- case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED:
- controlCb.onStoppedUnsupported();
- break;
- case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE:
- controlCb.onSupportAvailable();
- break;
- case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED:
- controlCb.onStoppedLimitReached();
- break;
- default:
- log.e("Unsupported OffloadCallbackEvent: " + event);
- }
+ handleOnEvent(event);
+ });
+ }
+
+ @Override
+ public void onEvent_1_1(int event) {
+ if (mOffloadControlVersion < OFFLOAD_HAL_VERSION_1_1) {
+ Log.wtf(TAG, "onEvent_1_1(" + event + ") fired on HAL "
+ + halVerToString(mOffloadControlVersion));
+ return;
+ }
+ handler.post(() -> {
+ handleOnEvent(event);
});
}
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 8db4cb9..0e8b2b5 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -128,7 +128,6 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.MessageUtils;
@@ -147,8 +146,11 @@
import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
/**
*
@@ -166,6 +168,9 @@
};
private static final SparseArray<String> sMagicDecoderRing =
MessageUtils.findMessageNames(sMessageClasses);
+
+ private static final int DUMP_TIMEOUT_MS = 10_000;
+
// Keep in sync with NETID_UNSET in system/netd/include/netid_client.h
private static final int NETID_UNSET = 0;
@@ -212,9 +217,6 @@
private final SparseArray<TetheringRequestParcel> mActiveTetheringRequests =
new SparseArray<>();
- // used to synchronize public access to members
- // TODO(b/153621704): remove mPublicSync to make Tethering lock free
- private final Object mPublicSync;
private final Context mContext;
private final ArrayMap<String, TetherState> mTetherStates;
private final BroadcastReceiver mStateReceiver;
@@ -240,8 +242,6 @@
private final BpfCoordinator mBpfCoordinator;
private final PrivateAddressCoordinator mPrivateAddressCoordinator;
private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
- // All the usage of mTetheringEventCallback should run in the same thread.
- private ITetheringEventCallback mTetheringEventCallback = null;
private volatile TetheringConfiguration mConfig;
private InterfaceSet mCurrentUpstreamIfaceSet;
@@ -255,11 +255,8 @@
private String mWifiP2pTetherInterface = null;
private int mOffloadStatus = TETHER_HARDWARE_OFFLOAD_STOPPED;
- @GuardedBy("mPublicSync")
private EthernetManager.TetheredInterfaceRequest mEthernetIfaceRequest;
- @GuardedBy("mPublicSync")
private String mConfiguredEthernetIface;
- @GuardedBy("mPublicSync")
private EthernetCallback mEthernetCallback;
public Tethering(TetheringDependencies deps) {
@@ -270,8 +267,6 @@
mLooper = mDeps.getTetheringLooper();
mNotificationUpdater = mDeps.getNotificationUpdater(mContext, mLooper);
- mPublicSync = new Object();
-
mTetherStates = new ArrayMap<>();
mConnectedClientsTracker = new ConnectedClientsTracker();
@@ -498,20 +493,18 @@
// Never called directly: only called from interfaceLinkStateChanged.
// See NetlinkHandler.cpp: notifyInterfaceChanged.
if (VDBG) Log.d(TAG, "interfaceStatusChanged " + iface + ", " + up);
- synchronized (mPublicSync) {
- if (up) {
- maybeTrackNewInterfaceLocked(iface);
+ if (up) {
+ maybeTrackNewInterfaceLocked(iface);
+ } else {
+ if (ifaceNameToType(iface) == TETHERING_BLUETOOTH
+ || ifaceNameToType(iface) == TETHERING_WIGIG) {
+ stopTrackingInterfaceLocked(iface);
} else {
- if (ifaceNameToType(iface) == TETHERING_BLUETOOTH
- || ifaceNameToType(iface) == TETHERING_WIGIG) {
- stopTrackingInterfaceLocked(iface);
- } else {
- // Ignore usb0 down after enabling RNDIS.
- // We will handle disconnect in interfaceRemoved.
- // Similarly, ignore interface down for WiFi. We monitor WiFi AP status
- // through the WifiManager.WIFI_AP_STATE_CHANGED_ACTION intent.
- if (VDBG) Log.d(TAG, "ignore interface down for " + iface);
- }
+ // Ignore usb0 down after enabling RNDIS.
+ // We will handle disconnect in interfaceRemoved.
+ // Similarly, ignore interface down for WiFi. We monitor WiFi AP status
+ // through the WifiManager.WIFI_AP_STATE_CHANGED_ACTION intent.
+ if (VDBG) Log.d(TAG, "ignore interface down for " + iface);
}
}
}
@@ -541,16 +534,12 @@
void interfaceAdded(String iface) {
if (VDBG) Log.d(TAG, "interfaceAdded " + iface);
- synchronized (mPublicSync) {
- maybeTrackNewInterfaceLocked(iface);
- }
+ maybeTrackNewInterfaceLocked(iface);
}
void interfaceRemoved(String iface) {
if (VDBG) Log.d(TAG, "interfaceRemoved " + iface);
- synchronized (mPublicSync) {
- stopTrackingInterfaceLocked(iface);
- }
+ stopTrackingInterfaceLocked(iface);
}
void startTethering(final TetheringRequestParcel request, final IIntResultListener listener) {
@@ -635,17 +624,15 @@
private int setWifiTethering(final boolean enable) {
final long ident = Binder.clearCallingIdentity();
try {
- synchronized (mPublicSync) {
- final WifiManager mgr = getWifiManager();
- if (mgr == null) {
- mLog.e("setWifiTethering: failed to get WifiManager!");
- return TETHER_ERROR_SERVICE_UNAVAIL;
- }
- if ((enable && mgr.startTetheredHotspot(null /* use existing softap config */))
- || (!enable && mgr.stopSoftAp())) {
- mWifiTetherRequested = enable;
- return TETHER_ERROR_NO_ERROR;
- }
+ final WifiManager mgr = getWifiManager();
+ if (mgr == null) {
+ mLog.e("setWifiTethering: failed to get WifiManager!");
+ return TETHER_ERROR_SERVICE_UNAVAIL;
+ }
+ if ((enable && mgr.startTetheredHotspot(null /* use existing softap config */))
+ || (!enable && mgr.stopSoftAp())) {
+ mWifiTetherRequested = enable;
+ return TETHER_ERROR_NO_ERROR;
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -697,18 +684,16 @@
private int setEthernetTethering(final boolean enable) {
final EthernetManager em = (EthernetManager) mContext.getSystemService(
Context.ETHERNET_SERVICE);
- synchronized (mPublicSync) {
- if (enable) {
- if (mEthernetCallback != null) {
- Log.d(TAG, "Ethernet tethering already started");
- return TETHER_ERROR_NO_ERROR;
- }
-
- mEthernetCallback = new EthernetCallback();
- mEthernetIfaceRequest = em.requestTetheredInterface(mExecutor, mEthernetCallback);
- } else {
- stopEthernetTetheringLocked();
+ if (enable) {
+ if (mEthernetCallback != null) {
+ Log.d(TAG, "Ethernet tethering already started");
+ return TETHER_ERROR_NO_ERROR;
}
+
+ mEthernetCallback = new EthernetCallback();
+ mEthernetIfaceRequest = em.requestTetheredInterface(mExecutor, mEthernetCallback);
+ } else {
+ stopEthernetTetheringLocked();
}
return TETHER_ERROR_NO_ERROR;
}
@@ -728,26 +713,22 @@
private class EthernetCallback implements EthernetManager.TetheredInterfaceCallback {
@Override
public void onAvailable(String iface) {
- synchronized (mPublicSync) {
- if (this != mEthernetCallback) {
- // Ethernet callback arrived after Ethernet tethering stopped. Ignore.
- return;
- }
- maybeTrackNewInterfaceLocked(iface, TETHERING_ETHERNET);
- changeInterfaceState(iface, getRequestedState(TETHERING_ETHERNET));
- mConfiguredEthernetIface = iface;
+ if (this != mEthernetCallback) {
+ // Ethernet callback arrived after Ethernet tethering stopped. Ignore.
+ return;
}
+ maybeTrackNewInterfaceLocked(iface, TETHERING_ETHERNET);
+ changeInterfaceState(iface, getRequestedState(TETHERING_ETHERNET));
+ mConfiguredEthernetIface = iface;
}
@Override
public void onUnavailable() {
- synchronized (mPublicSync) {
- if (this != mEthernetCallback) {
- // onAvailable called after stopping Ethernet tethering.
- return;
- }
- stopEthernetTetheringLocked();
+ if (this != mEthernetCallback) {
+ // onAvailable called after stopping Ethernet tethering.
+ return;
}
+ stopEthernetTetheringLocked();
}
}
@@ -761,57 +742,53 @@
private int tether(String iface, int requestedState) {
if (DBG) Log.d(TAG, "Tethering " + iface);
- synchronized (mPublicSync) {
- TetherState tetherState = mTetherStates.get(iface);
- if (tetherState == null) {
- Log.e(TAG, "Tried to Tether an unknown iface: " + iface + ", ignoring");
- return TETHER_ERROR_UNKNOWN_IFACE;
- }
- // Ignore the error status of the interface. If the interface is available,
- // the errors are referring to past tethering attempts anyway.
- if (tetherState.lastState != IpServer.STATE_AVAILABLE) {
- Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring");
- return TETHER_ERROR_UNAVAIL_IFACE;
- }
- // NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's queue but not yet
- // processed, this will be a no-op and it will not return an error.
- //
- // This code cannot race with untether() because they both synchronize on mPublicSync.
- // TODO: reexamine the threading and messaging model to totally remove mPublicSync.
- final int type = tetherState.ipServer.interfaceType();
- final TetheringRequestParcel request = mActiveTetheringRequests.get(type, null);
- if (request != null) {
- mActiveTetheringRequests.delete(type);
- }
- tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState, 0,
- request);
- return TETHER_ERROR_NO_ERROR;
+ TetherState tetherState = mTetherStates.get(iface);
+ if (tetherState == null) {
+ Log.e(TAG, "Tried to Tether an unknown iface: " + iface + ", ignoring");
+ return TETHER_ERROR_UNKNOWN_IFACE;
}
+ // Ignore the error status of the interface. If the interface is available,
+ // the errors are referring to past tethering attempts anyway.
+ if (tetherState.lastState != IpServer.STATE_AVAILABLE) {
+ Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring");
+ return TETHER_ERROR_UNAVAIL_IFACE;
+ }
+ // NOTE: If a CMD_TETHER_REQUESTED message is already in the TISM's queue but not yet
+ // processed, this will be a no-op and it will not return an error.
+ //
+ // This code cannot race with untether() because they both run on the handler thread.
+ final int type = tetherState.ipServer.interfaceType();
+ final TetheringRequestParcel request = mActiveTetheringRequests.get(type, null);
+ if (request != null) {
+ mActiveTetheringRequests.delete(type);
+ }
+ tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_REQUESTED, requestedState, 0,
+ request);
+ return TETHER_ERROR_NO_ERROR;
}
void untether(String iface, final IIntResultListener listener) {
mHandler.post(() -> {
try {
listener.onResult(untether(iface));
- } catch (RemoteException e) { }
+ } catch (RemoteException e) {
+ }
});
}
int untether(String iface) {
if (DBG) Log.d(TAG, "Untethering " + iface);
- synchronized (mPublicSync) {
- TetherState tetherState = mTetherStates.get(iface);
- if (tetherState == null) {
- Log.e(TAG, "Tried to Untether an unknown iface :" + iface + ", ignoring");
- return TETHER_ERROR_UNKNOWN_IFACE;
- }
- if (!tetherState.isCurrentlyServing()) {
- Log.e(TAG, "Tried to untether an inactive iface :" + iface + ", ignoring");
- return TETHER_ERROR_UNAVAIL_IFACE;
- }
- tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_UNREQUESTED);
- return TETHER_ERROR_NO_ERROR;
+ TetherState tetherState = mTetherStates.get(iface);
+ if (tetherState == null) {
+ Log.e(TAG, "Tried to Untether an unknown iface :" + iface + ", ignoring");
+ return TETHER_ERROR_UNKNOWN_IFACE;
}
+ if (!tetherState.isCurrentlyServing()) {
+ Log.e(TAG, "Tried to untether an inactive iface :" + iface + ", ignoring");
+ return TETHER_ERROR_UNAVAIL_IFACE;
+ }
+ tetherState.ipServer.sendMessage(IpServer.CMD_TETHER_UNREQUESTED);
+ return TETHER_ERROR_NO_ERROR;
}
void untetherAll() {
@@ -822,16 +799,15 @@
stopTethering(TETHERING_ETHERNET);
}
- int getLastTetherError(String iface) {
- synchronized (mPublicSync) {
- TetherState tetherState = mTetherStates.get(iface);
- if (tetherState == null) {
- Log.e(TAG, "Tried to getLastTetherError on an unknown iface :" + iface
- + ", ignoring");
- return TETHER_ERROR_UNKNOWN_IFACE;
- }
- return tetherState.lastError;
+ @VisibleForTesting
+ int getLastErrorForTest(String iface) {
+ TetherState tetherState = mTetherStates.get(iface);
+ if (tetherState == null) {
+ Log.e(TAG, "Tried to getLastErrorForTest on an unknown iface :" + iface
+ + ", ignoring");
+ return TETHER_ERROR_UNKNOWN_IFACE;
}
+ return tetherState.lastError;
}
private boolean isProvisioningNeededButUnavailable() {
@@ -888,27 +864,25 @@
mTetherStatesParcel = new TetherStatesParcel();
int downstreamTypesMask = DOWNSTREAM_NONE;
- synchronized (mPublicSync) {
- for (int i = 0; i < mTetherStates.size(); i++) {
- TetherState tetherState = mTetherStates.valueAt(i);
- String iface = mTetherStates.keyAt(i);
- if (tetherState.lastError != TETHER_ERROR_NO_ERROR) {
- erroredList.add(iface);
- lastErrorList.add(tetherState.lastError);
- } else if (tetherState.lastState == IpServer.STATE_AVAILABLE) {
- availableList.add(iface);
- } else if (tetherState.lastState == IpServer.STATE_LOCAL_ONLY) {
- localOnlyList.add(iface);
- } else if (tetherState.lastState == IpServer.STATE_TETHERED) {
- if (cfg.isUsb(iface)) {
- downstreamTypesMask |= (1 << TETHERING_USB);
- } else if (cfg.isWifi(iface)) {
- downstreamTypesMask |= (1 << TETHERING_WIFI);
- } else if (cfg.isBluetooth(iface)) {
- downstreamTypesMask |= (1 << TETHERING_BLUETOOTH);
- }
- tetherList.add(iface);
+ for (int i = 0; i < mTetherStates.size(); i++) {
+ TetherState tetherState = mTetherStates.valueAt(i);
+ String iface = mTetherStates.keyAt(i);
+ if (tetherState.lastError != TETHER_ERROR_NO_ERROR) {
+ erroredList.add(iface);
+ lastErrorList.add(tetherState.lastError);
+ } else if (tetherState.lastState == IpServer.STATE_AVAILABLE) {
+ availableList.add(iface);
+ } else if (tetherState.lastState == IpServer.STATE_LOCAL_ONLY) {
+ localOnlyList.add(iface);
+ } else if (tetherState.lastState == IpServer.STATE_TETHERED) {
+ if (cfg.isUsb(iface)) {
+ downstreamTypesMask |= (1 << TETHERING_USB);
+ } else if (cfg.isWifi(iface)) {
+ downstreamTypesMask |= (1 << TETHERING_WIFI);
+ } else if (cfg.isBluetooth(iface)) {
+ downstreamTypesMask |= (1 << TETHERING_BLUETOOTH);
}
+ tetherList.add(iface);
}
}
@@ -1006,21 +980,19 @@
// functions are ready to use.
//
// For more explanation, see b/62552150 .
- synchronized (Tethering.this.mPublicSync) {
- if (!usbConnected && mRndisEnabled) {
- // Turn off tethering if it was enabled and there is a disconnect.
- tetherMatchingInterfaces(IpServer.STATE_AVAILABLE, TETHERING_USB);
- mEntitlementMgr.stopProvisioningIfNeeded(TETHERING_USB);
- } else if (usbConfigured && rndisEnabled) {
- // Tether if rndis is enabled and usb is configured.
- final int state = getRequestedState(TETHERING_USB);
- tetherMatchingInterfaces(state, TETHERING_USB);
- } else if (usbConnected && ncmEnabled) {
- final int state = getRequestedState(TETHERING_NCM);
- tetherMatchingInterfaces(state, TETHERING_NCM);
- }
- mRndisEnabled = usbConfigured && rndisEnabled;
+ if (!usbConnected && mRndisEnabled) {
+ // Turn off tethering if it was enabled and there is a disconnect.
+ tetherMatchingInterfaces(IpServer.STATE_AVAILABLE, TETHERING_USB);
+ mEntitlementMgr.stopProvisioningIfNeeded(TETHERING_USB);
+ } else if (usbConfigured && rndisEnabled) {
+ // Tether if rndis is enabled and usb is configured.
+ final int state = getRequestedState(TETHERING_USB);
+ tetherMatchingInterfaces(state, TETHERING_USB);
+ } else if (usbConnected && ncmEnabled) {
+ final int state = getRequestedState(TETHERING_NCM);
+ tetherMatchingInterfaces(state, TETHERING_NCM);
}
+ mRndisEnabled = usbConfigured && rndisEnabled;
}
private void handleWifiApAction(Intent intent) {
@@ -1028,23 +1000,21 @@
final String ifname = intent.getStringExtra(EXTRA_WIFI_AP_INTERFACE_NAME);
final int ipmode = intent.getIntExtra(EXTRA_WIFI_AP_MODE, IFACE_IP_MODE_UNSPECIFIED);
- synchronized (Tethering.this.mPublicSync) {
- switch (curState) {
- case WifiManager.WIFI_AP_STATE_ENABLING:
- // We can see this state on the way to both enabled and failure states.
- break;
- case WifiManager.WIFI_AP_STATE_ENABLED:
- enableWifiIpServingLocked(ifname, ipmode);
- break;
- case WifiManager.WIFI_AP_STATE_DISABLING:
- // We can see this state on the way to disabled.
- break;
- case WifiManager.WIFI_AP_STATE_DISABLED:
- case WifiManager.WIFI_AP_STATE_FAILED:
- default:
- disableWifiIpServingLocked(ifname, curState);
- break;
- }
+ switch (curState) {
+ case WifiManager.WIFI_AP_STATE_ENABLING:
+ // We can see this state on the way to both enabled and failure states.
+ break;
+ case WifiManager.WIFI_AP_STATE_ENABLED:
+ enableWifiIpServingLocked(ifname, ipmode);
+ break;
+ case WifiManager.WIFI_AP_STATE_DISABLING:
+ // We can see this state on the way to disabled.
+ break;
+ case WifiManager.WIFI_AP_STATE_DISABLED:
+ case WifiManager.WIFI_AP_STATE_FAILED:
+ default:
+ disableWifiIpServingLocked(ifname, curState);
+ break;
}
}
@@ -1065,32 +1035,30 @@
Log.d(TAG, "WifiP2pAction: P2pInfo: " + p2pInfo + " Group: " + group);
}
- synchronized (Tethering.this.mPublicSync) {
- // if no group is formed, bring it down if needed.
- if (p2pInfo == null || !p2pInfo.groupFormed) {
- disableWifiP2pIpServingLockedIfNeeded(mWifiP2pTetherInterface);
- mWifiP2pTetherInterface = null;
- return;
- }
-
- // If there is a group but the device is not the owner, bail out.
- if (!isGroupOwner(group)) return;
-
- // If already serving from the correct interface, nothing to do.
- if (group.getInterface().equals(mWifiP2pTetherInterface)) return;
-
- // If already serving from another interface, turn it down first.
- if (!TextUtils.isEmpty(mWifiP2pTetherInterface)) {
- mLog.w("P2P tethered interface " + mWifiP2pTetherInterface
- + "is different from current interface "
- + group.getInterface() + ", re-tether it");
- disableWifiP2pIpServingLockedIfNeeded(mWifiP2pTetherInterface);
- }
-
- // Finally bring up serving on the new interface
- mWifiP2pTetherInterface = group.getInterface();
- enableWifiIpServingLocked(mWifiP2pTetherInterface, IFACE_IP_MODE_LOCAL_ONLY);
+ // if no group is formed, bring it down if needed.
+ if (p2pInfo == null || !p2pInfo.groupFormed) {
+ disableWifiP2pIpServingLockedIfNeeded(mWifiP2pTetherInterface);
+ mWifiP2pTetherInterface = null;
+ return;
}
+
+ // If there is a group but the device is not the owner, bail out.
+ if (!isGroupOwner(group)) return;
+
+ // If already serving from the correct interface, nothing to do.
+ if (group.getInterface().equals(mWifiP2pTetherInterface)) return;
+
+ // If already serving from another interface, turn it down first.
+ if (!TextUtils.isEmpty(mWifiP2pTetherInterface)) {
+ mLog.w("P2P tethered interface " + mWifiP2pTetherInterface
+ + "is different from current interface "
+ + group.getInterface() + ", re-tether it");
+ disableWifiP2pIpServingLockedIfNeeded(mWifiP2pTetherInterface);
+ }
+
+ // Finally bring up serving on the new interface
+ mWifiP2pTetherInterface = group.getInterface();
+ enableWifiIpServingLocked(mWifiP2pTetherInterface, IFACE_IP_MODE_LOCAL_ONLY);
}
private void handleUserRestrictionAction() {
@@ -1125,14 +1093,14 @@
@VisibleForTesting
protected static class UserRestrictionActionListener {
private final UserManager mUserMgr;
- private final Tethering mWrapper;
+ private final Tethering mTethering;
private final TetheringNotificationUpdater mNotificationUpdater;
public boolean mDisallowTethering;
- public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering wrapper,
+ public UserRestrictionActionListener(@NonNull UserManager um, @NonNull Tethering tethering,
@NonNull TetheringNotificationUpdater updater) {
mUserMgr = um;
- mWrapper = wrapper;
+ mTethering = tethering;
mNotificationUpdater = updater;
mDisallowTethering = false;
}
@@ -1159,13 +1127,13 @@
return;
}
- if (mWrapper.isTetheringActive()) {
+ if (mTethering.isTetheringActive()) {
// Restricted notification is shown when tethering function is disallowed on
// user's device.
mNotificationUpdater.notifyTetheringDisabledByRestriction();
// Untether from all downstreams since tethering is disallowed.
- mWrapper.untetherAll();
+ mTethering.untetherAll();
}
// TODO(b/148139325): send tetheringSupported on restriction change
}
@@ -1312,86 +1280,51 @@
return hasDownstreamConfiguration && hasUpstreamConfiguration;
}
- // TODO - update callers to use getTetheringConfiguration(),
- // which has only final members.
- String[] getTetherableUsbRegexs() {
- return copy(mConfig.tetherableUsbRegexs);
+ void setUsbTethering(boolean enable, IIntResultListener listener) {
+ mHandler.post(() -> {
+ try {
+ listener.onResult(setUsbTethering(enable));
+ } catch (RemoteException e) { }
+ });
}
- String[] getTetherableWifiRegexs() {
- return copy(mConfig.tetherableWifiRegexs);
- }
-
- String[] getTetherableBluetoothRegexs() {
- return copy(mConfig.tetherableBluetoothRegexs);
- }
-
- int setUsbTethering(boolean enable) {
+ private int setUsbTethering(boolean enable) {
if (VDBG) Log.d(TAG, "setUsbTethering(" + enable + ")");
UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
if (usbManager == null) {
mLog.e("setUsbTethering: failed to get UsbManager!");
return TETHER_ERROR_SERVICE_UNAVAIL;
}
-
- synchronized (mPublicSync) {
- usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_RNDIS
- : UsbManager.FUNCTION_NONE);
- }
+ usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_RNDIS
+ : UsbManager.FUNCTION_NONE);
return TETHER_ERROR_NO_ERROR;
}
private int setNcmTethering(boolean enable) {
if (VDBG) Log.d(TAG, "setNcmTethering(" + enable + ")");
UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
- synchronized (mPublicSync) {
- usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_NCM
- : UsbManager.FUNCTION_NONE);
- }
+ usbManager.setCurrentFunctions(enable ? UsbManager.FUNCTION_NCM : UsbManager.FUNCTION_NONE);
return TETHER_ERROR_NO_ERROR;
}
// TODO review API - figure out how to delete these entirely.
String[] getTetheredIfaces() {
ArrayList<String> list = new ArrayList<String>();
- synchronized (mPublicSync) {
- for (int i = 0; i < mTetherStates.size(); i++) {
- TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.lastState == IpServer.STATE_TETHERED) {
- list.add(mTetherStates.keyAt(i));
- }
+ for (int i = 0; i < mTetherStates.size(); i++) {
+ TetherState tetherState = mTetherStates.valueAt(i);
+ if (tetherState.lastState == IpServer.STATE_TETHERED) {
+ list.add(mTetherStates.keyAt(i));
}
}
return list.toArray(new String[list.size()]);
}
- String[] getTetherableIfaces() {
+ String[] getTetherableIfacesForTest() {
ArrayList<String> list = new ArrayList<String>();
- synchronized (mPublicSync) {
- for (int i = 0; i < mTetherStates.size(); i++) {
- TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.lastState == IpServer.STATE_AVAILABLE) {
- list.add(mTetherStates.keyAt(i));
- }
- }
- }
- return list.toArray(new String[list.size()]);
- }
-
- String[] getTetheredDhcpRanges() {
- // TODO: this is only valid for the old DHCP server. Latest search suggests it is only used
- // by WifiP2pServiceImpl to start dnsmasq: remove/deprecate after migrating callers.
- return mConfig.legacyDhcpRanges;
- }
-
- String[] getErroredIfaces() {
- ArrayList<String> list = new ArrayList<String>();
- synchronized (mPublicSync) {
- for (int i = 0; i < mTetherStates.size(); i++) {
- TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.lastError != TETHER_ERROR_NO_ERROR) {
- list.add(mTetherStates.keyAt(i));
- }
+ for (int i = 0; i < mTetherStates.size(); i++) {
+ TetherState tetherState = mTetherStates.valueAt(i);
+ if (tetherState.lastState == IpServer.STATE_AVAILABLE) {
+ list.add(mTetherStates.keyAt(i));
}
}
return list.toArray(new String[list.size()]);
@@ -1403,10 +1336,7 @@
private boolean upstreamWanted() {
if (!mForwardedDownstreams.isEmpty()) return true;
-
- synchronized (mPublicSync) {
- return mWifiTetherRequested;
- }
+ return mWifiTetherRequested;
}
// Needed because the canonical source of upstream truth is just the
@@ -2110,8 +2040,7 @@
}
private void startTrackDefaultNetwork() {
- mUpstreamNetworkMonitor.startTrackDefaultNetwork(mDeps.getDefaultNetworkRequest(),
- mEntitlementMgr);
+ mUpstreamNetworkMonitor.startTrackDefaultNetwork(mEntitlementMgr);
}
/** Get the latest value of the tethering entitlement check. */
@@ -2278,15 +2207,10 @@
pw.decreaseIndent();
}
- void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) {
+ void doDump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) {
// Binder.java closes the resource for us.
- @SuppressWarnings("resource")
- final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump.");
- return;
- }
+ @SuppressWarnings("resource") final IndentingPrintWriter pw = new IndentingPrintWriter(
+ writer, " ");
if (argsContain(args, "bpf")) {
dumpBpf(pw);
@@ -2307,37 +2231,35 @@
mEntitlementMgr.dump(pw);
pw.decreaseIndent();
- synchronized (mPublicSync) {
- pw.println("Tether state:");
- pw.increaseIndent();
- for (int i = 0; i < mTetherStates.size(); i++) {
- final String iface = mTetherStates.keyAt(i);
- final TetherState tetherState = mTetherStates.valueAt(i);
- pw.print(iface + " - ");
+ pw.println("Tether state:");
+ pw.increaseIndent();
+ for (int i = 0; i < mTetherStates.size(); i++) {
+ final String iface = mTetherStates.keyAt(i);
+ final TetherState tetherState = mTetherStates.valueAt(i);
+ pw.print(iface + " - ");
- switch (tetherState.lastState) {
- case IpServer.STATE_UNAVAILABLE:
- pw.print("UnavailableState");
- break;
- case IpServer.STATE_AVAILABLE:
- pw.print("AvailableState");
- break;
- case IpServer.STATE_TETHERED:
- pw.print("TetheredState");
- break;
- case IpServer.STATE_LOCAL_ONLY:
- pw.print("LocalHotspotState");
- break;
- default:
- pw.print("UnknownState");
- break;
- }
- pw.println(" - lastError = " + tetherState.lastError);
+ switch (tetherState.lastState) {
+ case IpServer.STATE_UNAVAILABLE:
+ pw.print("UnavailableState");
+ break;
+ case IpServer.STATE_AVAILABLE:
+ pw.print("AvailableState");
+ break;
+ case IpServer.STATE_TETHERED:
+ pw.print("TetheredState");
+ break;
+ case IpServer.STATE_LOCAL_ONLY:
+ pw.print("LocalHotspotState");
+ break;
+ default:
+ pw.print("UnknownState");
+ break;
}
- pw.println("Upstream wanted: " + upstreamWanted());
- pw.println("Current upstream interface(s): " + mCurrentUpstreamIfaceSet);
- pw.decreaseIndent();
+ pw.println(" - lastError = " + tetherState.lastError);
}
+ pw.println("Upstream wanted: " + upstreamWanted());
+ pw.println("Current upstream interface(s): " + mCurrentUpstreamIfaceSet);
+ pw.decreaseIndent();
pw.println("Hardware offload:");
pw.increaseIndent();
@@ -2363,6 +2285,40 @@
pw.decreaseIndent();
}
+ void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PERMISSION_GRANTED) {
+ writer.println("Permission Denial: can't dump.");
+ return;
+ }
+
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ // Don't crash the system if something in doDump throws an exception, but try to propagate
+ // the exception to the caller.
+ AtomicReference<RuntimeException> exceptionRef = new AtomicReference<>();
+ mHandler.post(() -> {
+ try {
+ doDump(fd, writer, args);
+ } catch (RuntimeException e) {
+ exceptionRef.set(e);
+ }
+ latch.countDown();
+ });
+
+ try {
+ if (!latch.await(DUMP_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ writer.println("Dump timeout after " + DUMP_TIMEOUT_MS + "ms");
+ return;
+ }
+ } catch (InterruptedException e) {
+ exceptionRef.compareAndSet(null, new IllegalStateException("Dump interrupted", e));
+ }
+
+ final RuntimeException e = exceptionRef.get();
+ if (e != null) throw e;
+ }
+
private static boolean argsContain(String[] args, String target) {
for (String arg : args) {
if (target.equals(arg)) return true;
@@ -2404,14 +2360,12 @@
// TODO: Move into TetherMainSM.
private void notifyInterfaceStateChange(IpServer who, int state, int error) {
final String iface = who.interfaceName();
- synchronized (mPublicSync) {
- final TetherState tetherState = mTetherStates.get(iface);
- if (tetherState != null && tetherState.ipServer.equals(who)) {
- tetherState.lastState = state;
- tetherState.lastError = error;
- } else {
- if (DBG) Log.d(TAG, "got notification from stale iface " + iface);
- }
+ final TetherState tetherState = mTetherStates.get(iface);
+ if (tetherState != null && tetherState.ipServer.equals(who)) {
+ tetherState.lastState = state;
+ tetherState.lastError = error;
+ } else {
+ if (DBG) Log.d(TAG, "got notification from stale iface " + iface);
}
mLog.log(String.format("OBSERVED iface=%s state=%s error=%s", iface, state, error));
@@ -2443,14 +2397,12 @@
private void notifyLinkPropertiesChanged(IpServer who, LinkProperties newLp) {
final String iface = who.interfaceName();
final int state;
- synchronized (mPublicSync) {
- final TetherState tetherState = mTetherStates.get(iface);
- if (tetherState != null && tetherState.ipServer.equals(who)) {
- state = tetherState.lastState;
- } else {
- mLog.log("got notification from stale iface " + iface);
- return;
- }
+ final TetherState tetherState = mTetherStates.get(iface);
+ if (tetherState != null && tetherState.ipServer.equals(who)) {
+ state = tetherState.lastState;
+ } else {
+ mLog.log("got notification from stale iface " + iface);
+ return;
}
mLog.log(String.format(
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index 45b9141..7df9475 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -20,7 +20,6 @@
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.net.INetd;
-import android.net.NetworkRequest;
import android.net.ip.IpServer;
import android.net.util.SharedLog;
import android.os.Handler;
@@ -99,11 +98,6 @@
}
/**
- * Get the NetworkRequest that should be fulfilled by the default network.
- */
- public abstract NetworkRequest getDefaultNetworkRequest();
-
- /**
* Get a reference to the EntitlementManager to be used by tethering.
*/
public EntitlementManager getEntitlementManager(Context ctx, Handler h, SharedLog log,
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
index 3438b9b..92dd921 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
@@ -35,8 +35,6 @@
import android.net.INetworkStackConnector;
import android.net.ITetheringConnector;
import android.net.ITetheringEventCallback;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
import android.net.NetworkStack;
import android.net.TetheringRequestParcel;
import android.net.dhcp.DhcpServerCallbacks;
@@ -121,9 +119,7 @@
IIntResultListener listener) {
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
- try {
- listener.onResult(mTethering.setUsbTethering(enable));
- } catch (RemoteException e) { }
+ mTethering.setUsbTethering(enable, listener);
}
@Override
@@ -309,19 +305,6 @@
public TetheringDependencies makeTetheringDependencies() {
return new TetheringDependencies() {
@Override
- public NetworkRequest getDefaultNetworkRequest() {
- // TODO: b/147280869, add a proper system API to replace this.
- final NetworkRequest trackDefaultRequest = new NetworkRequest.Builder()
- .clearCapabilities()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .build();
- return trackDefaultRequest;
- }
-
- @Override
public Looper getTetheringLooper() {
final HandlerThread tetherThread = new HandlerThread("android.tethering");
tetherThread.start();
diff --git a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
index 9de4c87..e615334 100644
--- a/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
+++ b/Tethering/src/com/android/networkstack/tethering/UpstreamNetworkMonitor.java
@@ -47,6 +47,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.StateMachine;
+import com.android.networkstack.apishim.ConnectivityManagerShimImpl;
+import com.android.networkstack.apishim.common.ConnectivityManagerShim;
+import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
import java.util.HashMap;
import java.util.HashSet;
@@ -142,33 +145,28 @@
mWhat = what;
mLocalPrefixes = new HashSet<>();
mIsDefaultCellularUpstream = false;
- }
-
- @VisibleForTesting
- public UpstreamNetworkMonitor(
- ConnectivityManager cm, StateMachine tgt, SharedLog log, int what) {
- this((Context) null, tgt, log, what);
- mCM = cm;
+ mCM = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
}
/**
- * Tracking the system default network. This method should be called when system is ready.
+ * Tracking the system default network. This method should be only called once when system is
+ * ready, and the callback is never unregistered.
*
- * @param defaultNetworkRequest should be the same as ConnectivityService default request
* @param entitle a EntitlementManager object to communicate between EntitlementManager and
* UpstreamNetworkMonitor
*/
- public void startTrackDefaultNetwork(NetworkRequest defaultNetworkRequest,
- EntitlementManager entitle) {
-
- // defaultNetworkRequest is not really a "request", just a way of tracking the system
- // default network. It's guaranteed not to actually bring up any networks because it's
- // the should be the same request as the ConnectivityService default request, and thus
- // shares fate with it. We can't use registerDefaultNetworkCallback because it will not
- // track the system default network if there is a VPN that applies to our UID.
- if (mDefaultNetworkCallback == null) {
- mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_DEFAULT_INTERNET);
- cm().requestNetwork(defaultNetworkRequest, mDefaultNetworkCallback, mHandler);
+ public void startTrackDefaultNetwork(EntitlementManager entitle) {
+ if (mDefaultNetworkCallback != null) {
+ Log.wtf(TAG, "default network callback is already registered");
+ return;
+ }
+ ConnectivityManagerShim mCmShim = ConnectivityManagerShimImpl.newInstance(mContext);
+ mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_DEFAULT_INTERNET);
+ try {
+ mCmShim.registerSystemDefaultNetworkCallback(mDefaultNetworkCallback, mHandler);
+ } catch (UnsupportedApiLevelException e) {
+ Log.wtf(TAG, "registerSystemDefaultNetworkCallback is not supported");
+ return;
}
if (mEntitlementMgr == null) {
mEntitlementMgr = entitle;
diff --git a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
index fe4e696..de94cba 100644
--- a/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
+++ b/Tethering/tests/integration/src/android/net/EthernetTetheringTest.java
@@ -16,6 +16,7 @@
package android.net;
+import static android.Manifest.permission.ACCESS_NETWORK_STATE;
import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.TETHER_PRIVILEGED;
@@ -121,7 +122,7 @@
// Needed to create a TestNetworkInterface, to call requestTetheredInterface, and to receive
// tethered client callbacks.
mUiAutomation.adoptShellPermissionIdentity(
- MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED);
+ MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, TETHER_PRIVILEGED, ACCESS_NETWORK_STATE);
mRunTests = mTm.isTetheringSupported() && mEm != null;
assumeTrue(mRunTests);
diff --git a/Tethering/tests/jarjar-rules.txt b/Tethering/tests/jarjar-rules.txt
index c99ff7f..9cb143e 100644
--- a/Tethering/tests/jarjar-rules.txt
+++ b/Tethering/tests/jarjar-rules.txt
@@ -1,8 +1,8 @@
# Don't jar-jar the entire package because this test use some
# internal classes (like ArrayUtils in com.android.internal.util)
rule com.android.internal.util.BitUtils* com.android.networkstack.tethering.util.BitUtils@1
-rule com.android.internal.util.IndentingPrintWriter.java* com.android.networkstack.tethering.util.IndentingPrintWriter.java@1
-rule com.android.internal.util.IState.java* com.android.networkstack.tethering.util.IState.java@1
+rule com.android.internal.util.IndentingPrintWriter* com.android.networkstack.tethering.util.IndentingPrintWriter@1
+rule com.android.internal.util.IState* com.android.networkstack.tethering.util.IState@1
rule com.android.internal.util.MessageUtils* com.android.networkstack.tethering.util.MessageUtils@1
rule com.android.internal.util.State* com.android.networkstack.tethering.util.State@1
rule com.android.internal.util.StateMachine* com.android.networkstack.tethering.util.StateMachine@1
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
index 88f2054..d800816 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java
@@ -58,7 +58,6 @@
import android.app.usage.NetworkStatsManager;
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;
@@ -150,6 +149,7 @@
when(mHardware.setUpstreamParameters(anyString(), any(), any(), any())).thenReturn(true);
when(mHardware.getForwardedStats(any())).thenReturn(new ForwardedStats());
when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
+ when(mHardware.setDataWarningAndLimit(anyString(), anyLong(), anyLong())).thenReturn(true);
}
private void enableOffload() {
@@ -503,77 +503,167 @@
expectedUidStatsDiff);
}
+ /**
+ * Test OffloadController with different combinations of HAL and framework versions can set
+ * data warning and/or limit correctly.
+ */
@Test
- public void testSetInterfaceQuota() throws Exception {
+ public void testSetDataWarningAndLimit() throws Exception {
+ // Verify the OffloadController is called by R framework, where the framework doesn't send
+ // warning.
+ checkSetDataWarningAndLimit(false, OFFLOAD_HAL_VERSION_1_0);
+ checkSetDataWarningAndLimit(false, OFFLOAD_HAL_VERSION_1_1);
+ // Verify the OffloadController is called by S+ framework, where the framework sends
+ // warning along with limit.
+ checkSetDataWarningAndLimit(true, OFFLOAD_HAL_VERSION_1_0);
+ checkSetDataWarningAndLimit(true, OFFLOAD_HAL_VERSION_1_1);
+ }
+
+ private void checkSetDataWarningAndLimit(boolean isProviderSetWarning, int controlVersion)
+ throws Exception {
enableOffload();
final OffloadController offload =
- startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
+ startOffloadController(controlVersion, true /*expectStart*/);
final String ethernetIface = "eth1";
final String mobileIface = "rmnet_data0";
final long ethernetLimit = 12345;
+ final long mobileWarning = 123456;
final long mobileLimit = 12345678;
final LinkProperties lp = new LinkProperties();
lp.setInterfaceName(ethernetIface);
- offload.setUpstreamLinkProperties(lp);
final InOrder inOrder = inOrder(mHardware);
- when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true);
+ when(mHardware.setUpstreamParameters(
+ any(), any(), any(), any())).thenReturn(true);
when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true);
+ when(mHardware.setDataWarningAndLimit(anyString(), anyLong(), anyLong())).thenReturn(true);
+ offload.setUpstreamLinkProperties(lp);
+ // Applying an interface sends the initial quota to the hardware.
+ if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ inOrder.verify(mHardware).setDataWarningAndLimit(ethernetIface, Long.MAX_VALUE,
+ Long.MAX_VALUE);
+ } else {
+ inOrder.verify(mHardware).setDataLimit(ethernetIface, Long.MAX_VALUE);
+ }
+ inOrder.verifyNoMoreInteractions();
+
+ // Verify that set to unlimited again won't cause duplicated calls to the hardware.
+ if (isProviderSetWarning) {
+ mTetherStatsProvider.onSetWarningAndLimit(ethernetIface,
+ NetworkStatsProvider.QUOTA_UNLIMITED, NetworkStatsProvider.QUOTA_UNLIMITED);
+ } else {
+ mTetherStatsProvider.onSetLimit(ethernetIface, NetworkStatsProvider.QUOTA_UNLIMITED);
+ }
+ waitForIdle();
+ inOrder.verifyNoMoreInteractions();
// Applying an interface quota to the current upstream immediately sends it to the hardware.
- mTetherStatsProvider.onSetLimit(ethernetIface, ethernetLimit);
+ if (isProviderSetWarning) {
+ mTetherStatsProvider.onSetWarningAndLimit(ethernetIface,
+ NetworkStatsProvider.QUOTA_UNLIMITED, ethernetLimit);
+ } else {
+ mTetherStatsProvider.onSetLimit(ethernetIface, ethernetLimit);
+ }
waitForIdle();
- inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit);
+ if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ inOrder.verify(mHardware).setDataWarningAndLimit(ethernetIface, Long.MAX_VALUE,
+ ethernetLimit);
+ } else {
+ inOrder.verify(mHardware).setDataLimit(ethernetIface, ethernetLimit);
+ }
inOrder.verifyNoMoreInteractions();
// Applying an interface quota to another upstream does not take any immediate action.
- mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
+ if (isProviderSetWarning) {
+ mTetherStatsProvider.onSetWarningAndLimit(mobileIface, mobileWarning, mobileLimit);
+ } else {
+ mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
+ }
waitForIdle();
- inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
+ if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ inOrder.verify(mHardware, never()).setDataWarningAndLimit(anyString(), anyLong(),
+ anyLong());
+ } else {
+ inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
+ }
// Switching to that upstream causes the quota to be applied if the parameters were applied
// correctly.
lp.setInterfaceName(mobileIface);
offload.setUpstreamLinkProperties(lp);
waitForIdle();
- inOrder.verify(mHardware).setDataLimit(mobileIface, mobileLimit);
+ if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ inOrder.verify(mHardware).setDataWarningAndLimit(mobileIface,
+ isProviderSetWarning ? mobileWarning : Long.MAX_VALUE,
+ mobileLimit);
+ } else {
+ inOrder.verify(mHardware).setDataLimit(mobileIface, mobileLimit);
+ }
- // Setting a limit of ITetheringStatsProvider.QUOTA_UNLIMITED causes the limit to be set
+ // Setting a limit of NetworkStatsProvider.QUOTA_UNLIMITED causes the limit to be set
// to Long.MAX_VALUE.
- mTetherStatsProvider.onSetLimit(mobileIface, ITetheringStatsProvider.QUOTA_UNLIMITED);
+ if (isProviderSetWarning) {
+ mTetherStatsProvider.onSetWarningAndLimit(mobileIface,
+ NetworkStatsProvider.QUOTA_UNLIMITED, NetworkStatsProvider.QUOTA_UNLIMITED);
+ } else {
+ mTetherStatsProvider.onSetLimit(mobileIface, NetworkStatsProvider.QUOTA_UNLIMITED);
+ }
waitForIdle();
- inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE);
+ if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ inOrder.verify(mHardware).setDataWarningAndLimit(mobileIface, Long.MAX_VALUE,
+ Long.MAX_VALUE);
+ } else {
+ inOrder.verify(mHardware).setDataLimit(mobileIface, Long.MAX_VALUE);
+ }
- // If setting upstream parameters fails, then the data limit is not set.
+ // If setting upstream parameters fails, then the data warning and limit is not set.
when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(false);
lp.setInterfaceName(ethernetIface);
offload.setUpstreamLinkProperties(lp);
- mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
+ if (isProviderSetWarning) {
+ mTetherStatsProvider.onSetWarningAndLimit(mobileIface, mobileWarning, mobileLimit);
+ } else {
+ mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
+ }
waitForIdle();
inOrder.verify(mHardware, never()).setDataLimit(anyString(), anyLong());
+ inOrder.verify(mHardware, never()).setDataWarningAndLimit(anyString(), anyLong(),
+ anyLong());
- // If setting the data limit fails while changing upstreams, offload is stopped.
+ // If setting the data warning and/or limit fails while changing upstreams, offload is
+ // stopped.
when(mHardware.setUpstreamParameters(any(), any(), any(), any())).thenReturn(true);
when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(false);
+ when(mHardware.setDataWarningAndLimit(anyString(), anyLong(), anyLong())).thenReturn(false);
lp.setInterfaceName(mobileIface);
offload.setUpstreamLinkProperties(lp);
- mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
+ if (isProviderSetWarning) {
+ mTetherStatsProvider.onSetWarningAndLimit(mobileIface, mobileWarning, mobileLimit);
+ } else {
+ mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit);
+ }
waitForIdle();
inOrder.verify(mHardware).getForwardedStats(ethernetIface);
inOrder.verify(mHardware).stopOffloadControl();
}
@Test
- public void testDataLimitCallback() throws Exception {
+ public void testDataWarningAndLimitCallback() throws Exception {
enableOffload();
- final OffloadController offload =
- startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
+ startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/);
OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
callback.onStoppedLimitReached();
mTetherStatsProviderCb.expectNotifyStatsUpdated();
+ mTetherStatsProviderCb.expectNotifyWarningOrLimitReached();
+
+ startOffloadController(OFFLOAD_HAL_VERSION_1_1, true /*expectStart*/);
+ callback = mControlCallbackCaptor.getValue();
+ callback.onWarningReached();
+ mTetherStatsProviderCb.expectNotifyStatsUpdated();
+ mTetherStatsProviderCb.expectNotifyWarningOrLimitReached();
}
@Test
@@ -761,9 +851,7 @@
// Initialize with fake eth upstream.
final String ethernetIface = "eth1";
InOrder inOrder = inOrder(mHardware);
- final LinkProperties lp = new LinkProperties();
- lp.setInterfaceName(ethernetIface);
- offload.setUpstreamLinkProperties(lp);
+ offload.setUpstreamLinkProperties(makeEthernetLinkProperties());
// Previous upstream was null, so no stats are fetched.
inOrder.verify(mHardware, never()).getForwardedStats(any());
@@ -796,4 +884,33 @@
mTetherStatsProviderCb.assertNoCallback();
verify(mHardware, never()).getForwardedStats(any());
}
+
+ private static LinkProperties makeEthernetLinkProperties() {
+ final String ethernetIface = "eth1";
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(ethernetIface);
+ return lp;
+ }
+
+ private void checkSoftwarePollingUsed(int controlVersion) throws Exception {
+ enableOffload();
+ setOffloadPollInterval(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS);
+ OffloadController offload =
+ startOffloadController(controlVersion, true /*expectStart*/);
+ offload.setUpstreamLinkProperties(makeEthernetLinkProperties());
+ mTetherStatsProvider.onSetAlert(0);
+ waitForIdle();
+ if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) {
+ mTetherStatsProviderCb.assertNoCallback();
+ } else {
+ mTetherStatsProviderCb.expectNotifyAlertReached();
+ }
+ verify(mHardware, never()).getForwardedStats(any());
+ }
+
+ @Test
+ public void testSoftwarePollingUsed() throws Exception {
+ checkSoftwarePollingUsed(OFFLOAD_HAL_VERSION_1_0);
+ checkSoftwarePollingUsed(OFFLOAD_HAL_VERSION_1_1);
+ }
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
index f4194e5..a8b3b92 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java
@@ -22,22 +22,27 @@
import static android.system.OsConstants.SOCK_STREAM;
import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0;
+import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_1;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.hardware.tetheroffload.config.V1_0.IOffloadConfig;
import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
-import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
-import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
+import android.hardware.tetheroffload.control.V1_1.ITetheringOffloadCallback;
+import android.hardware.tetheroffload.control.V1_1.OffloadCallbackEvent;
import android.net.netlink.StructNfGenMsg;
import android.net.netlink.StructNlMsgHdr;
import android.net.util.SharedLog;
@@ -56,6 +61,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -76,7 +82,7 @@
private OffloadHardwareInterface.ControlCallback mControlCallback;
@Mock private IOffloadConfig mIOffloadConfig;
- @Mock private IOffloadControl mIOffloadControl;
+ private IOffloadControl mIOffloadControl;
@Mock private NativeHandle mNativeHandle;
// Random values to test Netlink message.
@@ -84,8 +90,10 @@
private static final short TEST_FLAGS = 263;
class MyDependencies extends OffloadHardwareInterface.Dependencies {
- MyDependencies(SharedLog log) {
+ private final int mMockControlVersion;
+ MyDependencies(SharedLog log, final int mockControlVersion) {
super(log);
+ mMockControlVersion = mockControlVersion;
}
@Override
@@ -95,7 +103,19 @@
@Override
public Pair<IOffloadControl, Integer> getOffloadControl() {
- return new Pair<IOffloadControl, Integer>(mIOffloadControl, OFFLOAD_HAL_VERSION_1_0);
+ switch (mMockControlVersion) {
+ case OFFLOAD_HAL_VERSION_1_0:
+ mIOffloadControl = mock(IOffloadControl.class);
+ break;
+ case OFFLOAD_HAL_VERSION_1_1:
+ mIOffloadControl =
+ mock(android.hardware.tetheroffload.control.V1_1.IOffloadControl.class);
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid offload control version "
+ + mMockControlVersion);
+ }
+ return new Pair<IOffloadControl, Integer>(mIOffloadControl, mMockControlVersion);
}
@Override
@@ -107,14 +127,13 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- final SharedLog log = new SharedLog("test");
- mOffloadHw = new OffloadHardwareInterface(new Handler(mTestLooper.getLooper()), log,
- new MyDependencies(log));
mControlCallback = spy(new OffloadHardwareInterface.ControlCallback());
}
- // TODO: Pass version to test version specific operations.
- private void startOffloadHardwareInterface() throws Exception {
+ private void startOffloadHardwareInterface(int controlVersion) throws Exception {
+ final SharedLog log = new SharedLog("test");
+ mOffloadHw = new OffloadHardwareInterface(new Handler(mTestLooper.getLooper()), log,
+ new MyDependencies(log, controlVersion));
mOffloadHw.initOffloadConfig();
mOffloadHw.initOffloadControl(mControlCallback);
final ArgumentCaptor<ITetheringOffloadCallback> mOffloadCallbackCaptor =
@@ -125,7 +144,7 @@
@Test
public void testGetForwardedStats() throws Exception {
- startOffloadHardwareInterface();
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
final OffloadHardwareInterface.ForwardedStats stats = mOffloadHw.getForwardedStats(RMNET0);
verify(mIOffloadControl).getForwardedStats(eq(RMNET0), any());
assertNotNull(stats);
@@ -133,7 +152,7 @@
@Test
public void testSetLocalPrefixes() throws Exception {
- startOffloadHardwareInterface();
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
final ArrayList<String> localPrefixes = new ArrayList<>();
localPrefixes.add("127.0.0.0/8");
localPrefixes.add("fe80::/64");
@@ -143,15 +162,32 @@
@Test
public void testSetDataLimit() throws Exception {
- startOffloadHardwareInterface();
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
final long limit = 12345;
mOffloadHw.setDataLimit(RMNET0, limit);
verify(mIOffloadControl).setDataLimit(eq(RMNET0), eq(limit), any());
}
@Test
+ public void testSetDataWarningAndLimit() throws Exception {
+ // Verify V1.0 control HAL would reject the function call with exception.
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
+ final long warning = 12345;
+ final long limit = 67890;
+ assertThrows(IllegalArgumentException.class,
+ () -> mOffloadHw.setDataWarningAndLimit(RMNET0, warning, limit));
+ reset(mIOffloadControl);
+
+ // Verify V1.1 control HAL could receive this function call.
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_1);
+ mOffloadHw.setDataWarningAndLimit(RMNET0, warning, limit);
+ verify((android.hardware.tetheroffload.control.V1_1.IOffloadControl) mIOffloadControl)
+ .setDataWarningAndLimit(eq(RMNET0), eq(warning), eq(limit), any());
+ }
+
+ @Test
public void testSetUpstreamParameters() throws Exception {
- startOffloadHardwareInterface();
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
final String v4addr = "192.168.10.1";
final String v4gateway = "192.168.10.255";
final ArrayList<String> v6gws = new ArrayList<>(0);
@@ -170,7 +206,7 @@
@Test
public void testUpdateDownstreamPrefix() throws Exception {
- startOffloadHardwareInterface();
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
final String ifName = "wlan1";
final String prefix = "192.168.43.0/24";
mOffloadHw.addDownstreamPrefix(ifName, prefix);
@@ -182,7 +218,7 @@
@Test
public void testTetheringOffloadCallback() throws Exception {
- startOffloadHardwareInterface();
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STARTED);
mTestLooper.dispatchAll();
@@ -221,10 +257,26 @@
eq(uint16(udpParams.src.port)),
eq(udpParams.dst.addr),
eq(uint16(udpParams.dst.port)));
+ reset(mControlCallback);
+
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_1);
+
+ // Verify the interface will process the events that comes from V1.1 HAL.
+ mTetheringOffloadCallback.onEvent_1_1(OffloadCallbackEvent.OFFLOAD_STARTED);
+ mTestLooper.dispatchAll();
+ final InOrder inOrder = inOrder(mControlCallback);
+ inOrder.verify(mControlCallback).onStarted();
+ inOrder.verifyNoMoreInteractions();
+
+ mTetheringOffloadCallback.onEvent_1_1(OffloadCallbackEvent.OFFLOAD_WARNING_REACHED);
+ mTestLooper.dispatchAll();
+ inOrder.verify(mControlCallback).onWarningReached();
+ inOrder.verifyNoMoreInteractions();
}
@Test
public void testSendIpv4NfGenMsg() throws Exception {
+ startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0);
FileDescriptor writeSocket = new FileDescriptor();
FileDescriptor readSocket = new FileDescriptor();
try {
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java
index df82d7c..6090213 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TestConnectivityManager.java
@@ -16,6 +16,10 @@
package com.android.networkstack.tethering;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.fail;
@@ -64,12 +68,13 @@
public static final boolean CALLBACKS_FIRST = true;
final Map<NetworkCallback, NetworkRequestInfo> mAllCallbacks = new ArrayMap<>();
+ // This contains the callbacks tracking the system default network, whether it's registered
+ // with registerSystemDefaultNetworkCallback (S+) or with a custom request (R-).
final Map<NetworkCallback, NetworkRequestInfo> mTrackingDefault = new ArrayMap<>();
final Map<NetworkCallback, NetworkRequestInfo> mListening = new ArrayMap<>();
final Map<NetworkCallback, NetworkRequestInfo> mRequested = new ArrayMap<>();
final Map<NetworkCallback, Integer> mLegacyTypeMap = new ArrayMap<>();
- private final NetworkRequest mDefaultRequest;
private final Context mContext;
private int mNetworkId = 100;
@@ -80,13 +85,10 @@
* @param ctx the context to use. Must be a fake or a mock because otherwise the test will
* attempt to send real broadcasts and resulting in permission denials.
* @param svc an IConnectivityManager. Should be a fake or a mock.
- * @param defaultRequest the default NetworkRequest that will be used by Tethering.
*/
- public TestConnectivityManager(Context ctx, IConnectivityManager svc,
- NetworkRequest defaultRequest) {
+ public TestConnectivityManager(Context ctx, IConnectivityManager svc) {
super(ctx, svc);
mContext = ctx;
- mDefaultRequest = defaultRequest;
}
class NetworkRequestInfo {
@@ -181,11 +183,19 @@
makeDefaultNetwork(agent, BROADCAST_FIRST, null /* inBetween */);
}
+ static boolean looksLikeDefaultRequest(NetworkRequest req) {
+ return req.hasCapability(NET_CAPABILITY_INTERNET)
+ && !req.hasCapability(NET_CAPABILITY_DUN)
+ && !req.hasTransport(TRANSPORT_CELLULAR);
+ }
+
@Override
public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) {
assertFalse(mAllCallbacks.containsKey(cb));
mAllCallbacks.put(cb, new NetworkRequestInfo(req, h));
- if (mDefaultRequest.equals(req)) {
+ // For R- devices, Tethering will invoke this function in 2 cases, one is to request mobile
+ // network, the other is to track system default network.
+ if (looksLikeDefaultRequest(req)) {
assertFalse(mTrackingDefault.containsKey(cb));
mTrackingDefault.put(cb, new NetworkRequestInfo(req, h));
} else {
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
index 7204ff6..941cd78 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
@@ -25,7 +25,10 @@
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -214,11 +217,15 @@
}
private void runSetUsbTethering(final TestTetheringResult result) throws Exception {
- when(mTethering.setUsbTethering(true /* enable */)).thenReturn(TETHER_ERROR_NO_ERROR);
+ doAnswer((invocation) -> {
+ final IIntResultListener listener = invocation.getArgument(1);
+ listener.onResult(TETHER_ERROR_NO_ERROR);
+ return null;
+ }).when(mTethering).setUsbTethering(anyBoolean(), any(IIntResultListener.class));
mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result);
verify(mTethering).isTetheringSupported();
- verify(mTethering).setUsbTethering(true /* enable */);
+ verify(mTethering).setUsbTethering(eq(true) /* enable */, any(IIntResultListener.class));
result.assertResult(TETHER_ERROR_NO_ERROR);
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
index 81ba450..69c1758 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -26,7 +26,6 @@
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
-import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
@@ -75,7 +74,6 @@
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.argThat;
@@ -276,8 +274,6 @@
private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
private TestConnectivityManager mCm;
- private NetworkRequest mNetworkRequest;
- private NetworkCallback mDefaultNetworkCallback;
private class TestContext extends BroadcastInterceptingContext {
TestContext(Context base) {
@@ -452,11 +448,6 @@
}
@Override
- public NetworkRequest getDefaultNetworkRequest() {
- return mNetworkRequest;
- }
-
- @Override
public EntitlementManager getEntitlementManager(Context ctx, Handler h, SharedLog log,
Runnable callback) {
mEntitleMgr = spy(super.getEntitlementManager(ctx, h, log, callback));
@@ -647,15 +638,7 @@
mServiceContext.registerReceiver(mBroadcastReceiver,
new IntentFilter(ACTION_TETHER_STATE_CHANGED));
- // TODO: add NOT_VCN_MANAGED here, but more importantly in the production code.
- // TODO: even better, change TetheringDependencies.getDefaultNetworkRequest() to use
- // registerSystemDefaultNetworkCallback() on S and above.
- NetworkCapabilities defaultCaps = new NetworkCapabilities()
- .addCapability(NET_CAPABILITY_INTERNET);
- mNetworkRequest = new NetworkRequest(defaultCaps, TYPE_NONE, 1 /* requestId */,
- NetworkRequest.Type.REQUEST);
- mCm = spy(new TestConnectivityManager(mServiceContext, mock(IConnectivityManager.class),
- mNetworkRequest));
+ mCm = spy(new TestConnectivityManager(mServiceContext, mock(IConnectivityManager.class)));
mTethering = makeTethering();
verify(mStatsManager, times(1)).registerNetworkStatsProvider(anyString(), any());
@@ -795,15 +778,13 @@
}
private void verifyDefaultNetworkRequestFiled() {
- ArgumentCaptor<NetworkCallback> captor = ArgumentCaptor.forClass(NetworkCallback.class);
- verify(mCm, times(1)).requestNetwork(eq(mNetworkRequest),
- captor.capture(), any(Handler.class));
- mDefaultNetworkCallback = captor.getValue();
- assertNotNull(mDefaultNetworkCallback);
-
+ ArgumentCaptor<NetworkRequest> reqCaptor = ArgumentCaptor.forClass(NetworkRequest.class);
+ verify(mCm, times(1)).requestNetwork(reqCaptor.capture(),
+ any(NetworkCallback.class), any(Handler.class));
+ assertTrue(TestConnectivityManager.looksLikeDefaultRequest(reqCaptor.getValue()));
// The default network request is only ever filed once.
verifyNoMoreInteractions(mCm);
- mUpstreamNetworkMonitor.startTrackDefaultNetwork(mNetworkRequest, mEntitleMgr);
+ mUpstreamNetworkMonitor.startTrackDefaultNetwork(mEntitleMgr);
verifyNoMoreInteractions(mCm);
}
@@ -943,7 +924,7 @@
verifyNoMoreInteractions(mWifiManager);
// Asking for the last error after the per-interface state machine
// has been reaped yields an unknown interface error.
- assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_WLAN_IFNAME));
+ assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastErrorForTest(TEST_WLAN_IFNAME));
}
/**
@@ -1476,7 +1457,7 @@
verifyNoMoreInteractions(mWifiManager);
// Asking for the last error after the per-interface state machine
// has been reaped yields an unknown interface error.
- assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_WLAN_IFNAME));
+ assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastErrorForTest(TEST_WLAN_IFNAME));
}
// TODO: Test with and without interfaceStatusChanged().
@@ -1945,7 +1926,7 @@
// There are 2 IpServer state change events: STATE_AVAILABLE -> STATE_LOCAL_ONLY
verify(mNotificationUpdater, times(2)).onDownstreamChanged(DOWNSTREAM_NONE);
- assertEquals(TETHER_ERROR_NO_ERROR, mTethering.getLastTetherError(TEST_P2P_IFNAME));
+ assertEquals(TETHER_ERROR_NO_ERROR, mTethering.getLastErrorForTest(TEST_P2P_IFNAME));
// Emulate externally-visible WifiP2pManager effects, when wifi p2p group
// is being removed.
@@ -1964,7 +1945,7 @@
verifyNoMoreInteractions(mNetd);
// Asking for the last error after the per-interface state machine
// has been reaped yields an unknown interface error.
- assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME));
+ assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastErrorForTest(TEST_P2P_IFNAME));
}
private void workingWifiP2pGroupClient(
@@ -1994,7 +1975,7 @@
verifyNoMoreInteractions(mNetd);
// Asking for the last error after the per-interface state machine
// has been reaped yields an unknown interface error.
- assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME));
+ assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastErrorForTest(TEST_P2P_IFNAME));
}
@Test
@@ -2025,7 +2006,7 @@
verify(mNetd, never()).networkAddInterface(INetd.LOCAL_NET_ID, TEST_P2P_IFNAME);
verify(mNetd, never()).ipfwdEnableForwarding(TETHERING_NAME);
verify(mNetd, never()).tetherStartWithConfiguration(any());
- assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastTetherError(TEST_P2P_IFNAME));
+ assertEquals(TETHER_ERROR_UNKNOWN_IFACE, mTethering.getLastErrorForTest(TEST_P2P_IFNAME));
}
@Test
public void workingWifiP2pGroupOwnerLegacyModeWithIfaceChanged() throws Exception {
@@ -2390,10 +2371,10 @@
mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true);
sendUsbBroadcast(true, true, true, TETHERING_USB);
- assertContains(Arrays.asList(mTethering.getTetherableIfaces()), TEST_USB_IFNAME);
- assertContains(Arrays.asList(mTethering.getTetherableIfaces()), TEST_ETH_IFNAME);
- assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_USB_IFNAME));
- assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastTetherError(TEST_ETH_IFNAME));
+ assertContains(Arrays.asList(mTethering.getTetherableIfacesForTest()), TEST_USB_IFNAME);
+ assertContains(Arrays.asList(mTethering.getTetherableIfacesForTest()), TEST_ETH_IFNAME);
+ assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastErrorForTest(TEST_USB_IFNAME));
+ assertEquals(TETHER_ERROR_IFACE_CFG_ERROR, mTethering.getLastErrorForTest(TEST_ETH_IFNAME));
}
@Test
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
index cee0365..ce4ba85 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/UpstreamNetworkMonitorTest.java
@@ -29,10 +29,10 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
-import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -66,6 +66,7 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -83,10 +84,6 @@
private static final boolean INCLUDES = true;
private static final boolean EXCLUDES = false;
- // Actual contents of the request don't matter for this test. The lack of
- // any specific TRANSPORT_* is sufficient to identify this request.
- private static final NetworkRequest sDefaultRequest = new NetworkRequest.Builder().build();
-
private static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities.Builder()
.addTransportType(TRANSPORT_CELLULAR).addCapability(NET_CAPABILITY_INTERNET).build();
private static final NetworkCapabilities DUN_CAPABILITIES = new NetworkCapabilities.Builder()
@@ -113,9 +110,10 @@
when(mLog.forSubComponent(anyString())).thenReturn(mLog);
when(mEntitleMgr.isCellularUpstreamPermitted()).thenReturn(true);
- mCM = spy(new TestConnectivityManager(mContext, mCS, sDefaultRequest));
+ mCM = spy(new TestConnectivityManager(mContext, mCS));
+ when(mContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE))).thenReturn(mCM);
mSM = new TestStateMachine(mLooper.getLooper());
- mUNM = new UpstreamNetworkMonitor(mCM, mSM, mLog, EVENT_UNM_UPDATE);
+ mUNM = new UpstreamNetworkMonitor(mContext, mSM, mLog, EVENT_UNM_UPDATE);
}
@After public void tearDown() throws Exception {
@@ -146,7 +144,7 @@
@Test
public void testDefaultNetworkIsTracked() throws Exception {
assertTrue(mCM.hasNoCallbacks());
- mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
+ mUNM.startTrackDefaultNetwork(mEntitleMgr);
mUNM.startObserveAllNetworks();
assertEquals(1, mCM.mTrackingDefault.size());
@@ -159,7 +157,7 @@
public void testListensForAllNetworks() throws Exception {
assertTrue(mCM.mListening.isEmpty());
- mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
+ mUNM.startTrackDefaultNetwork(mEntitleMgr);
mUNM.startObserveAllNetworks();
assertFalse(mCM.mListening.isEmpty());
assertTrue(mCM.isListeningForAll());
@@ -170,9 +168,17 @@
@Test
public void testCallbacksRegistered() {
- mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
+ mUNM.startTrackDefaultNetwork(mEntitleMgr);
+ // Verify the fired default request matches expectation.
+ final ArgumentCaptor<NetworkRequest> requestCaptor =
+ ArgumentCaptor.forClass(NetworkRequest.class);
verify(mCM, times(1)).requestNetwork(
- eq(sDefaultRequest), any(NetworkCallback.class), any(Handler.class));
+ requestCaptor.capture(), any(NetworkCallback.class), any(Handler.class));
+ // For R- devices, Tethering will invoke this function in 2 cases, one is to
+ // request mobile network, the other is to track system default network. Verify
+ // the request is the one tracks default network.
+ assertTrue(TestConnectivityManager.looksLikeDefaultRequest(requestCaptor.getValue()));
+
mUNM.startObserveAllNetworks();
verify(mCM, times(1)).registerNetworkCallback(
any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
@@ -293,7 +299,7 @@
final Collection<Integer> preferredTypes = new ArrayList<>();
preferredTypes.add(TYPE_WIFI);
- mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
+ mUNM.startTrackDefaultNetwork(mEntitleMgr);
mUNM.startObserveAllNetworks();
// There are no networks, so there is nothing to select.
assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
@@ -366,7 +372,7 @@
@Test
public void testGetCurrentPreferredUpstream() throws Exception {
- mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
+ mUNM.startTrackDefaultNetwork(mEntitleMgr);
mUNM.startObserveAllNetworks();
mUNM.setUpstreamConfig(true /* autoUpstream */, false /* dunRequired */);
mUNM.setTryCell(true);
@@ -438,7 +444,7 @@
@Test
public void testLocalPrefixes() throws Exception {
- mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
+ mUNM.startTrackDefaultNetwork(mEntitleMgr);
mUNM.startObserveAllNetworks();
// [0] Test minimum set of local prefixes.
@@ -549,7 +555,7 @@
// Mobile has higher pirority than wifi.
preferredTypes.add(TYPE_MOBILE_HIPRI);
preferredTypes.add(TYPE_WIFI);
- mUNM.startTrackDefaultNetwork(sDefaultRequest, mEntitleMgr);
+ mUNM.startTrackDefaultNetwork(mEntitleMgr);
mUNM.startObserveAllNetworks();
// Setup wifi and make wifi as default network.
final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, WIFI_CAPABILITIES);
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index f474e23..d9e3ccb 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -102,6 +102,7 @@
import android.net.NetworkInfo.State;
import android.net.NetworkRequest;
import android.net.NetworkUtils;
+import android.net.ProxyInfo;
import android.net.SocketKeepalive;
import android.net.TestNetworkInterface;
import android.net.TestNetworkManager;
@@ -1916,4 +1917,12 @@
assertNull(NetworkInformationShimImpl.newInstance()
.getCapabilityCarrierName(ConstantsShim.NET_CAPABILITY_NOT_VCN_MANAGED));
}
+
+ @Test
+ public void testSetGlobalProxy() {
+ assumeTrue(TestUtils.shouldTestSApis());
+ // Behavior is verified in gts. Verify exception thrown w/o permission.
+ assertThrows(SecurityException.class, () -> mCm.setGlobalProxy(
+ ProxyInfo.buildDirectProxy("example.com" /* host */, 8080 /* port */)));
+ }
}
diff --git a/tests/cts/net/src/android/net/cts/IpSecManagerTest.java b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
index 355b496..d08f6e9 100644
--- a/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
@@ -44,6 +44,11 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.testutils.SkipPresubmit;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.DatagramPacket;
@@ -52,10 +57,6 @@
import java.net.InetAddress;
import java.util.Arrays;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
@RunWith(AndroidJUnit4.class)
@AppModeFull(reason = "Socket cannot bind in instant app mode")
public class IpSecManagerTest extends IpSecBaseTest {
@@ -692,6 +693,7 @@
}
@Test
+ @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
public void testAesCbcHmacMd5Tcp6() throws Exception {
IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_MD5, getKey(128), 96);
@@ -724,6 +726,7 @@
}
@Test
+ @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
public void testAesCbcHmacSha1Tcp6() throws Exception {
IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA1, getKey(160), 96);
@@ -756,6 +759,7 @@
}
@Test
+ @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
public void testAesCbcHmacSha256Tcp6() throws Exception {
IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, getKey(256), 128);
@@ -788,6 +792,7 @@
}
@Test
+ @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
public void testAesCbcHmacSha384Tcp6() throws Exception {
IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA384, getKey(384), 192);
@@ -820,6 +825,7 @@
}
@Test
+ @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
public void testAesCbcHmacSha512Tcp6() throws Exception {
IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA512, getKey(512), 256);
@@ -852,6 +858,7 @@
}
@Test
+ @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
public void testAesGcm64Tcp6() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 64);
@@ -884,6 +891,7 @@
}
@Test
+ @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
public void testAesGcm96Tcp6() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 96);
@@ -916,6 +924,7 @@
}
@Test
+ @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
public void testAesGcm128Tcp6() throws Exception {
IpSecAlgorithm authCrypt =
new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
@@ -1110,6 +1119,7 @@
}
@Test
+ @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
public void testCryptTcp6() throws Exception {
IpSecAlgorithm crypt = new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, crypt, null, null, false, 1, false);
@@ -1117,6 +1127,7 @@
}
@Test
+ @SkipPresubmit(reason = "b/186608065 - kernel 5.10 regression in TrafficStats with ipsec")
public void testAuthTcp6() throws Exception {
IpSecAlgorithm auth = new IpSecAlgorithm(IpSecAlgorithm.AUTH_HMAC_SHA256, getKey(256), 128);
checkTransform(IPPROTO_TCP, IPV6_LOOPBACK, null, auth, null, false, 1, false);