Merge "Autogenerate connectivity jarjar rules"
diff --git a/Tethering/Android.bp b/Tethering/Android.bp
index 29f6e12..026ed54 100644
--- a/Tethering/Android.bp
+++ b/Tethering/Android.bp
@@ -33,6 +33,7 @@
":framework-connectivity-shared-srcs",
":tethering-module-utils-srcs",
":services-tethering-shared-srcs",
+ ":statslog-tethering-java-gen",
],
static_libs: [
"androidx.annotation_annotation",
@@ -223,3 +224,11 @@
apex_available: ["com.android.tethering"],
min_sdk_version: "30",
}
+
+genrule {
+ name: "statslog-tethering-java-gen",
+ tools: ["stats-log-api-gen"],
+ cmd: "$(location stats-log-api-gen) --java $(out) --module network_tethering" +
+ " --javaPackage com.android.networkstack.tethering.metrics --javaClass TetheringStatsLog",
+ out: ["com/android/networkstack/tethering/metrics/TetheringStatsLog.java"],
+}
\ No newline at end of file
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index c718f4c..437ed71 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -69,6 +69,7 @@
import com.android.networkstack.tethering.BpfCoordinator.Ipv6ForwardingRule;
import com.android.networkstack.tethering.PrivateAddressCoordinator;
import com.android.networkstack.tethering.TetheringConfiguration;
+import com.android.networkstack.tethering.metrics.TetheringMetrics;
import com.android.networkstack.tethering.util.InterfaceSet;
import com.android.networkstack.tethering.util.PrefixUtils;
@@ -282,13 +283,15 @@
private LinkAddress mIpv4Address;
+ private final TetheringMetrics mTetheringMetrics;
+
// TODO: Add a dependency object to pass the data members or variables from the tethering
// object. It helps to reduce the arguments of the constructor.
public IpServer(
String ifaceName, Looper looper, int interfaceType, SharedLog log,
INetd netd, @NonNull BpfCoordinator coordinator, Callback callback,
TetheringConfiguration config, PrivateAddressCoordinator addressCoordinator,
- Dependencies deps) {
+ TetheringMetrics tetheringMetrics, Dependencies deps) {
super(ifaceName, looper);
mLog = log.forSubComponent(ifaceName);
mNetd = netd;
@@ -303,6 +306,7 @@
mP2pLeasesSubnetPrefixLength = config.getP2pLeasesSubnetPrefixLength();
mPrivateAddressCoordinator = addressCoordinator;
mDeps = deps;
+ mTetheringMetrics = tetheringMetrics;
resetLinkProperties();
mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
mServingMode = STATE_AVAILABLE;
@@ -1201,6 +1205,9 @@
stopConntrackMonitoring();
resetLinkProperties();
+
+ mTetheringMetrics.updateErrorCode(mInterfaceType, mLastError);
+ mTetheringMetrics.sendReport(mInterfaceType);
}
@Override
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index ecb6478..c403548 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -77,6 +77,7 @@
import com.android.networkstack.tethering.apishim.common.BpfCoordinatorShim;
import com.android.networkstack.tethering.util.TetheringUtils.ForwardedStats;
+import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -1024,7 +1025,7 @@
map.forEach((k, v) -> {
pw.println(String.format("%s: %s", k, v));
});
- } catch (ErrnoException e) {
+ } catch (ErrnoException | IOException e) {
pw.println("Error dumping BPF stats map: " + e);
}
}
@@ -1072,7 +1073,7 @@
return;
}
map.forEach((k, v) -> pw.println(ipv6UpstreamRuletoString(k, v)));
- } catch (ErrnoException e) {
+ } catch (ErrnoException | IOException e) {
pw.println("Error dumping IPv6 upstream map: " + e);
}
}
@@ -1116,7 +1117,7 @@
if (CollectionUtils.contains(args, DUMPSYS_RAWMAP_ARG_STATS)) {
try (BpfMap<TetherStatsKey, TetherStatsValue> statsMap = mDeps.getBpfStatsMap()) {
dumpRawMap(statsMap, pw);
- } catch (ErrnoException e) {
+ } catch (ErrnoException | IOException e) {
pw.println("Error dumping stats map: " + e);
}
return;
@@ -1124,7 +1125,7 @@
if (CollectionUtils.contains(args, DUMPSYS_RAWMAP_ARG_UPSTREAM4)) {
try (BpfMap<Tether4Key, Tether4Value> upstreamMap = mDeps.getBpfUpstream4Map()) {
dumpRawMap(upstreamMap, pw);
- } catch (ErrnoException e) {
+ } catch (ErrnoException | IOException e) {
pw.println("Error dumping IPv4 map: " + e);
}
return;
@@ -1195,7 +1196,7 @@
pw.increaseIndent();
dumpIpv4ForwardingRuleMap(now, DOWNSTREAM, downstreamMap, pw);
pw.decreaseIndent();
- } catch (ErrnoException e) {
+ } catch (ErrnoException | IOException e) {
pw.println("Error dumping IPv4 map: " + e);
}
}
@@ -1220,7 +1221,7 @@
}
if (v.val > 0) pw.println(String.format("%s: %d", counterName, v.val));
});
- } catch (ErrnoException e) {
+ } catch (ErrnoException | IOException e) {
pw.println("Error dumping counter map: " + e);
}
}
@@ -1244,7 +1245,7 @@
pw.println(String.format("%d (%s) -> %d (%s)", k.ifIndex, getIfName(k.ifIndex),
v.ifIndex, getIfName(v.ifIndex)));
});
- } catch (ErrnoException e) {
+ } catch (ErrnoException | IOException e) {
pw.println("Error dumping dev map: " + e);
}
pw.decreaseIndent();
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index 44935fc..551fd63 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -140,6 +140,7 @@
import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceCallbackShim;
import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceRequestShim;
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
+import com.android.networkstack.tethering.metrics.TetheringMetrics;
import com.android.networkstack.tethering.util.InterfaceSet;
import com.android.networkstack.tethering.util.PrefixUtils;
import com.android.networkstack.tethering.util.TetheringUtils;
@@ -254,6 +255,7 @@
private final UserManager mUserManager;
private final BpfCoordinator mBpfCoordinator;
private final PrivateAddressCoordinator mPrivateAddressCoordinator;
+ private final TetheringMetrics mTetheringMetrics;
private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
private volatile TetheringConfiguration mConfig;
@@ -292,6 +294,7 @@
mNetd = mDeps.getINetd(mContext);
mLooper = mDeps.getTetheringLooper();
mNotificationUpdater = mDeps.getNotificationUpdater(mContext, mLooper);
+ mTetheringMetrics = mDeps.getTetheringMetrics();
// This is intended to ensrure that if something calls startTethering(bluetooth) just after
// bluetooth is enabled. Before onServiceConnected is called, store the calls into this
@@ -616,7 +619,8 @@
processInterfaceStateChange(iface, false /* enabled */);
}
- void startTethering(final TetheringRequestParcel request, final IIntResultListener listener) {
+ void startTethering(final TetheringRequestParcel request, final String callerPkg,
+ final IIntResultListener listener) {
mHandler.post(() -> {
final TetheringRequestParcel unfinishedRequest = mActiveTetheringRequests.get(
request.tetheringType);
@@ -636,6 +640,7 @@
request.showProvisioningUi);
}
enableTetheringInternal(request.tetheringType, true /* enabled */, listener);
+ mTetheringMetrics.createBuilder(request.tetheringType, callerPkg);
});
}
@@ -695,7 +700,11 @@
// If changing tethering fail, remove corresponding request
// no matter who trigger the start/stop.
- if (result != TETHER_ERROR_NO_ERROR) mActiveTetheringRequests.remove(type);
+ if (result != TETHER_ERROR_NO_ERROR) {
+ mActiveTetheringRequests.remove(type);
+ mTetheringMetrics.updateErrorCode(type, result);
+ mTetheringMetrics.sendReport(type);
+ }
}
private int setWifiTethering(final boolean enable) {
@@ -2749,7 +2758,7 @@
final TetherState tetherState = new TetherState(
new IpServer(iface, mLooper, interfaceType, mLog, mNetd, mBpfCoordinator,
makeControlCallback(), mConfig, mPrivateAddressCoordinator,
- mDeps.getIpServerDependencies()), isNcm);
+ mTetheringMetrics, mDeps.getIpServerDependencies()), isNcm);
mTetherStates.put(iface, tetherState);
tetherState.ipServer.start();
}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
index 9224213..8e0354d 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringDependencies.java
@@ -34,6 +34,7 @@
import com.android.internal.util.StateMachine;
import com.android.networkstack.apishim.BluetoothPanShimImpl;
import com.android.networkstack.apishim.common.BluetoothPanShim;
+import com.android.networkstack.tethering.metrics.TetheringMetrics;
import java.util.ArrayList;
@@ -163,4 +164,11 @@
public BluetoothPanShim getBluetoothPanShim(BluetoothPan pan) {
return BluetoothPanShimImpl.newInstance(pan);
}
+
+ /**
+ * Get a reference to the TetheringMetrics to be used by tethering.
+ */
+ public TetheringMetrics getTetheringMetrics() {
+ return new TetheringMetrics();
+ }
}
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
index 9fb61fe..f147e10 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
@@ -137,7 +137,7 @@
return;
}
- mTethering.startTethering(request, listener);
+ mTethering.startTethering(request, callerPkg, listener);
}
@Override
diff --git a/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java b/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
new file mode 100644
index 0000000..e25f2ae
--- /dev/null
+++ b/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.networkstack.tethering.metrics;
+
+import static android.net.TetheringManager.TETHERING_BLUETOOTH;
+import static android.net.TetheringManager.TETHERING_ETHERNET;
+import static android.net.TetheringManager.TETHERING_NCM;
+import static android.net.TetheringManager.TETHERING_USB;
+import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.net.TetheringManager.TETHERING_WIFI_P2P;
+import static android.net.TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_DISABLE_FORWARDING_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
+import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION;
+import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
+import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
+import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
+import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_UNAVAIL_IFACE;
+import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
+import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED;
+import static android.net.TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
+
+import android.stats.connectivity.DownstreamType;
+import android.stats.connectivity.ErrorCode;
+import android.stats.connectivity.UpstreamType;
+import android.stats.connectivity.UserType;
+import android.util.Log;
+import android.util.SparseArray;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+
+/**
+ * Collection of utilities for tethering metrics.
+ *
+ * To see if the logs are properly sent to statsd, execute following commands
+ *
+ * $ adb shell cmd stats print-logs
+ * $ adb logcat | grep statsd OR $ adb logcat -b stats
+ *
+ * @hide
+ */
+public class TetheringMetrics {
+ private static final String TAG = TetheringMetrics.class.getSimpleName();
+ private static final boolean DBG = false;
+ private static final String SETTINGS_PKG_NAME = "com.android.settings";
+ private static final String SYSTEMUI_PKG_NAME = "com.android.systemui";
+ private static final String GMS_PKG_NAME = "com.google.android.gms";
+ private final SparseArray<NetworkTetheringReported.Builder> mBuilderMap = new SparseArray<>();
+
+ /** Update Tethering stats about caller's package name and downstream type. */
+ public void createBuilder(final int downstreamType, final String callerPkg) {
+ mBuilderMap.clear();
+ NetworkTetheringReported.Builder statsBuilder =
+ NetworkTetheringReported.newBuilder();
+ statsBuilder.setDownstreamType(downstreamTypeToEnum(downstreamType))
+ .setUserType(userTypeToEnum(callerPkg))
+ .setUpstreamType(UpstreamType.UT_UNKNOWN)
+ .setErrorCode(ErrorCode.EC_NO_ERROR)
+ .build();
+ mBuilderMap.put(downstreamType, statsBuilder);
+ }
+
+ /** Update error code of given downstreamType. */
+ public void updateErrorCode(final int downstreamType, final int errCode) {
+ NetworkTetheringReported.Builder statsBuilder = mBuilderMap.get(downstreamType);
+ if (statsBuilder == null) {
+ Log.e(TAG, "Given downstreamType does not exist, this is a bug!");
+ return;
+ }
+ statsBuilder.setErrorCode(errorCodeToEnum(errCode));
+ }
+
+ /** Remove Tethering stats.
+ * If Tethering stats is ready to write then write it before removing.
+ */
+ public void sendReport(final int downstreamType) {
+ final NetworkTetheringReported.Builder statsBuilder =
+ mBuilderMap.get(downstreamType);
+ if (statsBuilder == null) {
+ Log.e(TAG, "Given downstreamType does not exist, this is a bug!");
+ return;
+ }
+ write(statsBuilder.build());
+ mBuilderMap.remove(downstreamType);
+ }
+
+ /** Collect Tethering stats and write metrics data to statsd pipeline. */
+ @VisibleForTesting
+ public void write(@NonNull final NetworkTetheringReported reported) {
+ TetheringStatsLog.write(TetheringStatsLog.NETWORK_TETHERING_REPORTED,
+ reported.getErrorCode().getNumber(),
+ reported.getDownstreamType().getNumber(),
+ reported.getUpstreamType().getNumber(),
+ reported.getUserType().getNumber());
+ if (DBG) {
+ Log.d(TAG, "Write errorCode: " + reported.getErrorCode().getNumber()
+ + ", downstreamType: " + reported.getDownstreamType().getNumber()
+ + ", upstreamType: " + reported.getUpstreamType().getNumber()
+ + ", userType: " + reported.getUserType().getNumber());
+ }
+ }
+
+ /** Map {@link TetheringType} to {@link DownstreamType} */
+ private DownstreamType downstreamTypeToEnum(final int ifaceType) {
+ switch(ifaceType) {
+ case TETHERING_WIFI:
+ return DownstreamType.DS_TETHERING_WIFI;
+ case TETHERING_WIFI_P2P:
+ return DownstreamType.DS_TETHERING_WIFI_P2P;
+ case TETHERING_USB:
+ return DownstreamType.DS_TETHERING_USB;
+ case TETHERING_BLUETOOTH:
+ return DownstreamType.DS_TETHERING_BLUETOOTH;
+ case TETHERING_NCM:
+ return DownstreamType.DS_TETHERING_NCM;
+ case TETHERING_ETHERNET:
+ return DownstreamType.DS_TETHERING_ETHERNET;
+ default:
+ return DownstreamType.DS_UNSPECIFIED;
+ }
+ }
+
+ /** Map {@link StartTetheringError} to {@link ErrorCode} */
+ private ErrorCode errorCodeToEnum(final int lastError) {
+ switch(lastError) {
+ case TETHER_ERROR_NO_ERROR:
+ return ErrorCode.EC_NO_ERROR;
+ case TETHER_ERROR_UNKNOWN_IFACE:
+ return ErrorCode.EC_UNKNOWN_IFACE;
+ case TETHER_ERROR_SERVICE_UNAVAIL:
+ return ErrorCode.EC_SERVICE_UNAVAIL;
+ case TETHER_ERROR_UNSUPPORTED:
+ return ErrorCode.EC_UNSUPPORTED;
+ case TETHER_ERROR_UNAVAIL_IFACE:
+ return ErrorCode.EC_UNAVAIL_IFACE;
+ case TETHER_ERROR_INTERNAL_ERROR:
+ return ErrorCode.EC_INTERNAL_ERROR;
+ case TETHER_ERROR_TETHER_IFACE_ERROR:
+ return ErrorCode.EC_TETHER_IFACE_ERROR;
+ case TETHER_ERROR_UNTETHER_IFACE_ERROR:
+ return ErrorCode.EC_UNTETHER_IFACE_ERROR;
+ case TETHER_ERROR_ENABLE_FORWARDING_ERROR:
+ return ErrorCode.EC_ENABLE_FORWARDING_ERROR;
+ case TETHER_ERROR_DISABLE_FORWARDING_ERROR:
+ return ErrorCode.EC_DISABLE_FORWARDING_ERROR;
+ case TETHER_ERROR_IFACE_CFG_ERROR:
+ return ErrorCode.EC_IFACE_CFG_ERROR;
+ case TETHER_ERROR_PROVISIONING_FAILED:
+ return ErrorCode.EC_PROVISIONING_FAILED;
+ case TETHER_ERROR_DHCPSERVER_ERROR:
+ return ErrorCode.EC_DHCPSERVER_ERROR;
+ case TETHER_ERROR_ENTITLEMENT_UNKNOWN:
+ return ErrorCode.EC_ENTITLEMENT_UNKNOWN;
+ case TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION:
+ return ErrorCode.EC_NO_CHANGE_TETHERING_PERMISSION;
+ case TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION:
+ return ErrorCode.EC_NO_ACCESS_TETHERING_PERMISSION;
+ default:
+ return ErrorCode.EC_UNKNOWN_TYPE;
+ }
+ }
+
+ /** Map callerPkg to {@link UserType} */
+ private UserType userTypeToEnum(final String callerPkg) {
+ if (callerPkg.equals(SETTINGS_PKG_NAME)) {
+ return UserType.USER_SETTINGS;
+ } else if (callerPkg.equals(SYSTEMUI_PKG_NAME)) {
+ return UserType.USER_SYSTEMUI;
+ } else if (callerPkg.equals(GMS_PKG_NAME)) {
+ return UserType.USER_GMS;
+ } else {
+ return UserType.USER_UNKNOWN;
+ }
+ }
+}
diff --git a/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java b/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
index ad2faa0..75c2ad1 100644
--- a/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
+++ b/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
@@ -358,8 +358,8 @@
try {
mTestMap.clear();
fail("clearing already-closed map should throw");
- } catch (ErrnoException expected) {
- assertEquals(OsConstants.EBADF, expected.errno);
+ } catch (IllegalStateException expected) {
+ // ParcelFileDescriptor.getFd throws IllegalStateException: Already closed.
}
}
diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index aac531a..bf7e887 100644
--- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -116,6 +116,7 @@
import com.android.networkstack.tethering.TetherLimitValue;
import com.android.networkstack.tethering.TetherUpstream6Key;
import com.android.networkstack.tethering.TetheringConfiguration;
+import com.android.networkstack.tethering.metrics.TetheringMetrics;
import com.android.networkstack.tethering.util.InterfaceSet;
import com.android.networkstack.tethering.util.PrefixUtils;
import com.android.testutils.DevSdkIgnoreRule;
@@ -186,6 +187,7 @@
@Mock private NetworkStatsManager mStatsManager;
@Mock private TetheringConfiguration mTetherConfig;
@Mock private ConntrackMonitor mConntrackMonitor;
+ @Mock private TetheringMetrics mTetheringMetrics;
@Mock private BpfMap<Tether4Key, Tether4Value> mBpfDownstream4Map;
@Mock private BpfMap<Tether4Key, Tether4Value> mBpfUpstream4Map;
@Mock private BpfMap<TetherDownstream6Key, Tether6Value> mBpfDownstream6Map;
@@ -235,7 +237,7 @@
when(mTetherConfig.getP2pLeasesSubnetPrefixLength()).thenReturn(P2P_SUBNET_PREFIX_LENGTH);
mIpServer = new IpServer(
IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, mBpfCoordinator,
- mCallback, mTetherConfig, mAddressCoordinator, mDependencies);
+ mCallback, mTetherConfig, mAddressCoordinator, mTetheringMetrics, mDependencies);
mIpServer.start();
mNeighborEventConsumer = neighborCaptor.getValue();
@@ -367,7 +369,7 @@
.thenReturn(mIpNeighborMonitor);
mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
mNetd, mBpfCoordinator, mCallback, mTetherConfig, mAddressCoordinator,
- mDependencies);
+ mTetheringMetrics, mDependencies);
mIpServer.start();
mLooper.dispatchAll();
verify(mCallback).updateInterfaceState(
@@ -451,6 +453,9 @@
mIpServer, STATE_AVAILABLE, TETHER_ERROR_NO_ERROR);
inOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), any(LinkProperties.class));
+ verify(mTetheringMetrics).updateErrorCode(eq(TETHERING_BLUETOOTH),
+ eq(TETHER_ERROR_NO_ERROR));
+ verify(mTetheringMetrics).sendReport(eq(TETHERING_BLUETOOTH));
verifyNoMoreInteractions(mNetd, mCallback, mAddressCoordinator);
}
@@ -658,6 +663,9 @@
usbTeardownOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), mLinkPropertiesCaptor.capture());
assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
+ verify(mTetheringMetrics).updateErrorCode(eq(TETHERING_USB),
+ eq(TETHER_ERROR_TETHER_IFACE_ERROR));
+ verify(mTetheringMetrics).sendReport(eq(TETHERING_USB));
}
@Test
@@ -676,6 +684,9 @@
usbTeardownOrder.verify(mCallback).updateLinkProperties(
eq(mIpServer), mLinkPropertiesCaptor.capture());
assertNoAddressesNorRoutes(mLinkPropertiesCaptor.getValue());
+ verify(mTetheringMetrics).updateErrorCode(eq(TETHERING_USB),
+ eq(TETHER_ERROR_ENABLE_FORWARDING_ERROR));
+ verify(mTetheringMetrics).sendReport(eq(TETHERING_USB));
}
@Test
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 f664d5d..9db8f16 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
@@ -275,7 +275,7 @@
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
verify(mTethering).isTetheringSupported();
- verify(mTethering).startTethering(eq(request), eq(result));
+ verify(mTethering).startTethering(eq(request), eq(TEST_CALLER_PKG), eq(result));
}
@Test
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 2fd7f48..4662c96 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java
@@ -45,6 +45,7 @@
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHERING_WIFI_P2P;
import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
@@ -192,6 +193,7 @@
import com.android.networkstack.apishim.common.BluetoothPanShim.TetheredInterfaceRequestShim;
import com.android.networkstack.apishim.common.UnsupportedApiLevelException;
import com.android.networkstack.tethering.TestConnectivityManager.TestNetworkAgent;
+import com.android.networkstack.tethering.metrics.TetheringMetrics;
import com.android.testutils.MiscAsserts;
import org.junit.After;
@@ -239,6 +241,7 @@
private static final String TEST_WIFI_REGEX = "test_wlan\\d";
private static final String TEST_P2P_REGEX = "test_p2p-p2p\\d-.*";
private static final String TEST_BT_REGEX = "test_pan\\d";
+ private static final String TEST_CALLER_PKG = "com.test.tethering";
private static final int CELLULAR_NETID = 100;
private static final int WIFI_NETID = 101;
@@ -273,6 +276,7 @@
@Mock private BluetoothPan mBluetoothPan;
@Mock private BluetoothPanShim mBluetoothPanShim;
@Mock private TetheredInterfaceRequestShim mTetheredInterfaceRequestShim;
+ @Mock private TetheringMetrics mTetheringMetrics;
private final MockIpServerDependencies mIpServerDependencies =
spy(new MockIpServerDependencies());
@@ -497,6 +501,11 @@
}
@Override
+ public TetheringMetrics getTetheringMetrics() {
+ return mTetheringMetrics;
+ }
+
+ @Override
public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx,
TetheringConfiguration cfg) {
mPrivateAddressCoordinator = super.getPrivateAddressCoordinator(ctx, cfg);
@@ -855,7 +864,8 @@
private void prepareNcmTethering() {
// Emulate startTethering(TETHERING_NCM) called
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), null);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), TEST_CALLER_PKG,
+ null);
mLooper.dispatchAll();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM);
}
@@ -863,7 +873,7 @@
private void prepareUsbTethering() {
// Emulate pressing the USB tethering button in Settings UI.
final TetheringRequestParcel request = createTetheringRequestParcel(TETHERING_USB);
- mTethering.startTethering(request, null);
+ mTethering.startTethering(request, TEST_CALLER_PKG, null);
mLooper.dispatchAll();
assertEquals(1, mTethering.getActiveTetheringRequests().size());
@@ -1433,7 +1443,8 @@
when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), TEST_CALLER_PKG,
+ null);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startTetheredHotspot(null);
verifyNoMoreInteractions(mWifiManager);
@@ -1460,7 +1471,8 @@
when(mWifiManager.startTetheredHotspot(any(SoftApConfiguration.class))).thenReturn(true);
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), TEST_CALLER_PKG,
+ null);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startTetheredHotspot(null);
verifyNoMoreInteractions(mWifiManager);
@@ -1536,11 +1548,13 @@
doThrow(new RemoteException()).when(mNetd).ipfwdEnableForwarding(TETHERING_NAME);
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), TEST_CALLER_PKG,
+ null);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startTetheredHotspot(null);
verifyNoMoreInteractions(mWifiManager);
verifyNoMoreInteractions(mNetd);
+ verify(mTetheringMetrics).createBuilder(eq(TETHERING_WIFI), anyString());
// Emulate externally-visible WifiManager effects, causing the
// per-interface state machine to start up, and telling us that
@@ -1579,6 +1593,10 @@
verify(mWifiManager).updateInterfaceIpState(
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR);
+ verify(mTetheringMetrics, times(2)).updateErrorCode(eq(TETHERING_WIFI),
+ eq(TETHER_ERROR_INTERNAL_ERROR));
+ verify(mTetheringMetrics, times(2)).sendReport(eq(TETHERING_WIFI));
+
verifyNoMoreInteractions(mWifiManager);
verifyNoMoreInteractions(mNetd);
}
@@ -1881,7 +1899,8 @@
tetherState = callback.pollTetherStatesChanged();
assertArrayEquals(tetherState.availableList, new TetheringInterface[] {wifiIface});
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), null);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_WIFI), TEST_CALLER_PKG,
+ null);
sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED);
tetherState = callback.pollTetherStatesChanged();
assertArrayEquals(tetherState.tetheredList, new TetheringInterface[] {wifiIface});
@@ -1978,10 +1997,12 @@
public void testNoDuplicatedEthernetRequest() throws Exception {
final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class);
when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest);
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), TEST_CALLER_PKG,
+ null);
mLooper.dispatchAll();
verify(mEm, times(1)).requestTetheredInterface(any(), any());
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), TEST_CALLER_PKG,
+ null);
mLooper.dispatchAll();
verifyNoMoreInteractions(mEm);
mTethering.stopTethering(TETHERING_ETHERNET);
@@ -2185,14 +2206,16 @@
final ResultListener thirdResult = new ResultListener(TETHER_ERROR_NO_ERROR);
// Enable USB tethering and check that Tethering starts USB.
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), firstResult);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), TEST_CALLER_PKG,
+ firstResult);
mLooper.dispatchAll();
firstResult.assertHasResult();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS);
verifyNoMoreInteractions(mUsbManager);
// Enable USB tethering again with the same request and expect no change to USB.
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), secondResult);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB), TEST_CALLER_PKG,
+ secondResult);
mLooper.dispatchAll();
secondResult.assertHasResult();
verify(mUsbManager, never()).setCurrentFunctions(UsbManager.FUNCTION_NONE);
@@ -2201,7 +2224,8 @@
// Enable USB tethering with a different request and expect that USB is stopped and
// started.
mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
- serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL), thirdResult);
+ serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL),
+ TEST_CALLER_PKG, thirdResult);
mLooper.dispatchAll();
thirdResult.assertHasResult();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NONE);
@@ -2230,7 +2254,8 @@
final ArgumentCaptor<DhcpServingParamsParcel> dhcpParamsCaptor =
ArgumentCaptor.forClass(DhcpServingParamsParcel.class);
mTethering.startTethering(createTetheringRequestParcel(TETHERING_USB,
- serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL), null);
+ serverLinkAddr, clientLinkAddr, false, CONNECTIVITY_SCOPE_GLOBAL),
+ TEST_CALLER_PKG, null);
mLooper.dispatchAll();
verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_NCM);
mTethering.interfaceStatusChanged(TEST_NCM_IFNAME, true);
@@ -2298,7 +2323,7 @@
final TetheringRequestParcel wifiNotExemptRequest =
createTetheringRequestParcel(TETHERING_WIFI, null, null, false,
CONNECTIVITY_SCOPE_GLOBAL);
- mTethering.startTethering(wifiNotExemptRequest, null);
+ mTethering.startTethering(wifiNotExemptRequest, TEST_CALLER_PKG, null);
mLooper.dispatchAll();
verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false);
verify(mEntitleMgr, never()).setExemptedDownstreamType(TETHERING_WIFI);
@@ -2312,7 +2337,7 @@
final TetheringRequestParcel wifiExemptRequest =
createTetheringRequestParcel(TETHERING_WIFI, null, null, true,
CONNECTIVITY_SCOPE_GLOBAL);
- mTethering.startTethering(wifiExemptRequest, null);
+ mTethering.startTethering(wifiExemptRequest, TEST_CALLER_PKG, null);
mLooper.dispatchAll();
verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false);
verify(mEntitleMgr).setExemptedDownstreamType(TETHERING_WIFI);
@@ -2325,14 +2350,14 @@
// If one app enables tethering without provisioning check first, then another app enables
// tethering of the same type but does not disable the provisioning check.
setupForRequiredProvisioning();
- mTethering.startTethering(wifiExemptRequest, null);
+ mTethering.startTethering(wifiExemptRequest, TEST_CALLER_PKG, null);
mLooper.dispatchAll();
verify(mEntitleMgr, never()).startProvisioningIfNeeded(TETHERING_WIFI, false);
verify(mEntitleMgr).setExemptedDownstreamType(TETHERING_WIFI);
assertTrue(mEntitleMgr.isCellularUpstreamPermitted());
reset(mEntitleMgr);
setupForRequiredProvisioning();
- mTethering.startTethering(wifiNotExemptRequest, null);
+ mTethering.startTethering(wifiNotExemptRequest, TEST_CALLER_PKG, null);
mLooper.dispatchAll();
verify(mEntitleMgr).startProvisioningIfNeeded(TETHERING_WIFI, false);
verify(mEntitleMgr, never()).setExemptedDownstreamType(TETHERING_WIFI);
@@ -2422,7 +2447,8 @@
when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest);
final ArgumentCaptor<TetheredInterfaceCallback> callbackCaptor =
ArgumentCaptor.forClass(TetheredInterfaceCallback.class);
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET), null);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_ETHERNET),
+ TEST_CALLER_PKG, null);
mLooper.dispatchAll();
verify(mEm).requestTetheredInterface(any(), callbackCaptor.capture());
TetheredInterfaceCallback ethCallback = callbackCaptor.getValue();
@@ -2597,7 +2623,8 @@
final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR);
mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), result);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH),
+ TEST_CALLER_PKG, result);
mLooper.dispatchAll();
verifySetBluetoothTethering(true /* enable */, true /* bindToPanService */);
result.assertHasResult();
@@ -2632,7 +2659,8 @@
final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR);
mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), result);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH),
+ TEST_CALLER_PKG, result);
mLooper.dispatchAll();
verifySetBluetoothTethering(true /* enable */, true /* bindToPanService */);
result.assertHasResult();
@@ -2653,7 +2681,8 @@
// already bound.
mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
final ResultListener secondResult = new ResultListener(TETHER_ERROR_NO_ERROR);
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), secondResult);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH),
+ TEST_CALLER_PKG, secondResult);
mLooper.dispatchAll();
verifySetBluetoothTethering(true /* enable */, false /* bindToPanService */);
secondResult.assertHasResult();
@@ -2674,7 +2703,8 @@
public void testBluetoothServiceDisconnects() throws Exception {
final ResultListener result = new ResultListener(TETHER_ERROR_NO_ERROR);
mockBluetoothSettings(true /* bluetoothOn */, true /* tetheringOn */);
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH), result);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_BLUETOOTH),
+ TEST_CALLER_PKG, result);
mLooper.dispatchAll();
ServiceListener panListener = verifySetBluetoothTethering(true /* enable */,
true /* bindToPanService */);
@@ -2825,18 +2855,26 @@
runNcmTethering();
verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
any(), any());
+ verify(mTetheringMetrics).createBuilder(eq(TETHERING_NCM), anyString());
// Change the USB tethering function to NCM. Because the USB tethering function was set to
// RNDIS (the default), tethering is stopped.
forceUsbTetheringUse(TETHER_USB_NCM_FUNCTION);
verifyUsbTetheringStopDueToSettingChange(TEST_NCM_IFNAME);
+ verify(mTetheringMetrics).updateErrorCode(anyInt(), eq(TETHER_ERROR_NO_ERROR));
+ verify(mTetheringMetrics).sendReport(eq(TETHERING_NCM));
// If TETHERING_USB is forced to use ncm function, TETHERING_NCM would no longer be
// available.
final ResultListener ncmResult = new ResultListener(TETHER_ERROR_SERVICE_UNAVAIL);
- mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), ncmResult);
+ mTethering.startTethering(createTetheringRequestParcel(TETHERING_NCM), TEST_CALLER_PKG,
+ ncmResult);
mLooper.dispatchAll();
ncmResult.assertHasResult();
+ verify(mTetheringMetrics, times(2)).createBuilder(eq(TETHERING_NCM), anyString());
+ verify(mTetheringMetrics).updateErrorCode(eq(TETHERING_NCM),
+ eq(TETHER_ERROR_SERVICE_UNAVAIL));
+ verify(mTetheringMetrics, times(2)).sendReport(eq(TETHERING_NCM));
// Run TETHERING_USB with ncm configuration.
runDualStackUsbTethering(TEST_NCM_IFNAME);
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
new file mode 100644
index 0000000..c34cf5f
--- /dev/null
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.networkstack.tethering.metrics;
+
+import static android.net.TetheringManager.TETHERING_BLUETOOTH;
+import static android.net.TetheringManager.TETHERING_ETHERNET;
+import static android.net.TetheringManager.TETHERING_NCM;
+import static android.net.TetheringManager.TETHERING_USB;
+import static android.net.TetheringManager.TETHERING_WIFI;
+import static android.net.TetheringManager.TETHERING_WIFI_P2P;
+import static android.net.TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_DISABLE_FORWARDING_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_ENABLE_FORWARDING_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
+import static android.net.TetheringManager.TETHER_ERROR_IFACE_CFG_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_INTERNAL_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION;
+import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
+import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED;
+import static android.net.TetheringManager.TETHER_ERROR_SERVICE_UNAVAIL;
+import static android.net.TetheringManager.TETHER_ERROR_TETHER_IFACE_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_UNAVAIL_IFACE;
+import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE;
+import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_TYPE;
+import static android.net.TetheringManager.TETHER_ERROR_UNSUPPORTED;
+import static android.net.TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
+
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.stats.connectivity.DownstreamType;
+import android.stats.connectivity.ErrorCode;
+import android.stats.connectivity.UpstreamType;
+import android.stats.connectivity.UserType;
+import android.util.Pair;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class TetheringMetricsTest {
+ private static final String TEST_CALLER_PKG = "com.test.caller.pkg";
+ private static final String SETTINGS_PKG = "com.android.settings";
+ private static final String SYSTEMUI_PKG = "com.android.systemui";
+ private static final String GMS_PKG = "com.google.android.gms";
+ private TetheringMetrics mTetheringMetrics;
+
+ private final NetworkTetheringReported.Builder mStatsBuilder =
+ NetworkTetheringReported.newBuilder();
+
+ private class MockTetheringMetrics extends TetheringMetrics {
+ @Override
+ public void write(final NetworkTetheringReported reported) { }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mTetheringMetrics = spy(new MockTetheringMetrics());
+ }
+
+ private void runDownstreamTypesTest(final Pair<Integer, DownstreamType>... testPairs)
+ throws Exception {
+ for (Pair<Integer, DownstreamType> testPair : testPairs) {
+ final int type = testPair.first;
+ final DownstreamType expectedResult = testPair.second;
+
+ mTetheringMetrics.createBuilder(type, TEST_CALLER_PKG);
+ mTetheringMetrics.updateErrorCode(type, TETHER_ERROR_NO_ERROR);
+ mTetheringMetrics.sendReport(type);
+ NetworkTetheringReported expectedReport =
+ mStatsBuilder.setDownstreamType(expectedResult)
+ .setUserType(UserType.USER_UNKNOWN)
+ .setUpstreamType(UpstreamType.UT_UNKNOWN)
+ .setErrorCode(ErrorCode.EC_NO_ERROR)
+ .build();
+ verify(mTetheringMetrics).write(expectedReport);
+ reset(mTetheringMetrics);
+ }
+ }
+
+ @Test
+ public void testDownstreamTypes() throws Exception {
+ runDownstreamTypesTest(new Pair<>(TETHERING_WIFI, DownstreamType.DS_TETHERING_WIFI),
+ new Pair<>(TETHERING_WIFI_P2P, DownstreamType.DS_TETHERING_WIFI_P2P),
+ new Pair<>(TETHERING_BLUETOOTH, DownstreamType.DS_TETHERING_BLUETOOTH),
+ new Pair<>(TETHERING_USB, DownstreamType.DS_TETHERING_USB),
+ new Pair<>(TETHERING_NCM, DownstreamType.DS_TETHERING_NCM),
+ new Pair<>(TETHERING_ETHERNET, DownstreamType.DS_TETHERING_ETHERNET));
+ }
+
+ private void runErrorCodesTest(final Pair<Integer, ErrorCode>... testPairs)
+ throws Exception {
+ for (Pair<Integer, ErrorCode> testPair : testPairs) {
+ final int errorCode = testPair.first;
+ final ErrorCode expectedResult = testPair.second;
+
+ mTetheringMetrics.createBuilder(TETHERING_WIFI, TEST_CALLER_PKG);
+ mTetheringMetrics.updateErrorCode(TETHERING_WIFI, errorCode);
+ mTetheringMetrics.sendReport(TETHERING_WIFI);
+ NetworkTetheringReported expectedReport =
+ mStatsBuilder.setDownstreamType(DownstreamType.DS_TETHERING_WIFI)
+ .setUserType(UserType.USER_UNKNOWN)
+ .setUpstreamType(UpstreamType.UT_UNKNOWN)
+ .setErrorCode(expectedResult)
+ .build();
+ verify(mTetheringMetrics).write(expectedReport);
+ reset(mTetheringMetrics);
+ }
+ }
+
+ @Test
+ public void testErrorCodes() throws Exception {
+ runErrorCodesTest(new Pair<>(TETHER_ERROR_NO_ERROR, ErrorCode.EC_NO_ERROR),
+ new Pair<>(TETHER_ERROR_UNKNOWN_IFACE, ErrorCode.EC_UNKNOWN_IFACE),
+ new Pair<>(TETHER_ERROR_SERVICE_UNAVAIL, ErrorCode.EC_SERVICE_UNAVAIL),
+ new Pair<>(TETHER_ERROR_UNSUPPORTED, ErrorCode.EC_UNSUPPORTED),
+ new Pair<>(TETHER_ERROR_UNAVAIL_IFACE, ErrorCode.EC_UNAVAIL_IFACE),
+ new Pair<>(TETHER_ERROR_INTERNAL_ERROR, ErrorCode.EC_INTERNAL_ERROR),
+ new Pair<>(TETHER_ERROR_TETHER_IFACE_ERROR, ErrorCode.EC_TETHER_IFACE_ERROR),
+ new Pair<>(TETHER_ERROR_UNTETHER_IFACE_ERROR, ErrorCode.EC_UNTETHER_IFACE_ERROR),
+ new Pair<>(TETHER_ERROR_ENABLE_FORWARDING_ERROR,
+ ErrorCode.EC_ENABLE_FORWARDING_ERROR),
+ new Pair<>(TETHER_ERROR_DISABLE_FORWARDING_ERROR,
+ ErrorCode.EC_DISABLE_FORWARDING_ERROR),
+ new Pair<>(TETHER_ERROR_IFACE_CFG_ERROR, ErrorCode.EC_IFACE_CFG_ERROR),
+ new Pair<>(TETHER_ERROR_PROVISIONING_FAILED, ErrorCode.EC_PROVISIONING_FAILED),
+ new Pair<>(TETHER_ERROR_DHCPSERVER_ERROR, ErrorCode.EC_DHCPSERVER_ERROR),
+ new Pair<>(TETHER_ERROR_ENTITLEMENT_UNKNOWN, ErrorCode.EC_ENTITLEMENT_UNKNOWN),
+ new Pair<>(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION,
+ ErrorCode.EC_NO_CHANGE_TETHERING_PERMISSION),
+ new Pair<>(TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION,
+ ErrorCode.EC_NO_ACCESS_TETHERING_PERMISSION),
+ new Pair<>(TETHER_ERROR_UNKNOWN_TYPE, ErrorCode.EC_UNKNOWN_TYPE));
+ }
+
+ private void runUserTypesTest(final Pair<String, UserType>... testPairs)
+ throws Exception {
+ for (Pair<String, UserType> testPair : testPairs) {
+ final String callerPkg = testPair.first;
+ final UserType expectedResult = testPair.second;
+
+ mTetheringMetrics.createBuilder(TETHERING_WIFI, callerPkg);
+ mTetheringMetrics.updateErrorCode(TETHERING_WIFI, TETHER_ERROR_NO_ERROR);
+ mTetheringMetrics.sendReport(TETHERING_WIFI);
+ NetworkTetheringReported expectedReport =
+ mStatsBuilder.setDownstreamType(DownstreamType.DS_TETHERING_WIFI)
+ .setUserType(expectedResult)
+ .setUpstreamType(UpstreamType.UT_UNKNOWN)
+ .setErrorCode(ErrorCode.EC_NO_ERROR)
+ .build();
+ verify(mTetheringMetrics).write(expectedReport);
+ reset(mTetheringMetrics);
+ }
+ }
+
+ @Test
+ public void testUserTypes() throws Exception {
+ runUserTypesTest(new Pair<>(TEST_CALLER_PKG, UserType.USER_UNKNOWN),
+ new Pair<>(SETTINGS_PKG, UserType.USER_SETTINGS),
+ new Pair<>(SYSTEMUI_PKG, UserType.USER_SYSTEMUI),
+ new Pair<>(GMS_PKG, UserType.USER_GMS));
+ }
+}
diff --git a/bpf_progs/netd.c b/bpf_progs/netd.c
index fe9a871..f3f675f 100644
--- a/bpf_progs/netd.c
+++ b/bpf_progs/netd.c
@@ -194,7 +194,7 @@
BpfConfig enabledRules = getConfig(UID_RULES_CONFIGURATION_KEY);
UidOwnerValue* uidEntry = bpf_uid_owner_map_lookup_elem(&uid);
- uint8_t uidRules = uidEntry ? uidEntry->rule : 0;
+ uint32_t uidRules = uidEntry ? uidEntry->rule : 0;
uint32_t allowed_iif = uidEntry ? uidEntry->iif : 0;
if (enabledRules) {
diff --git a/framework-t/src/android/net/NetworkStatsCollection.java b/framework-t/src/android/net/NetworkStatsCollection.java
index b59a890..29ea772 100644
--- a/framework-t/src/android/net/NetworkStatsCollection.java
+++ b/framework-t/src/android/net/NetworkStatsCollection.java
@@ -694,6 +694,26 @@
}
}
+ /**
+ * Remove histories which contains or is before the cutoff timestamp.
+ * @hide
+ */
+ public void removeHistoryBefore(long cutoffMillis) {
+ final ArrayList<Key> knownKeys = new ArrayList<>();
+ knownKeys.addAll(mStats.keySet());
+
+ for (Key key : knownKeys) {
+ final NetworkStatsHistory history = mStats.get(key);
+ if (history.getStart() > cutoffMillis) continue;
+
+ history.removeBucketsStartingBefore(cutoffMillis);
+ if (history.size() == 0) {
+ mStats.remove(key);
+ }
+ mDirty = true;
+ }
+ }
+
private void noteRecordedHistory(long startMillis, long endMillis, long totalBytes) {
if (startMillis < mStartMillis) mStartMillis = startMillis;
if (endMillis > mEndMillis) mEndMillis = endMillis;
diff --git a/framework-t/src/android/net/NetworkStatsHistory.java b/framework-t/src/android/net/NetworkStatsHistory.java
index 301fef9..b45d44d 100644
--- a/framework-t/src/android/net/NetworkStatsHistory.java
+++ b/framework-t/src/android/net/NetworkStatsHistory.java
@@ -680,19 +680,21 @@
}
/**
- * Remove buckets older than requested cutoff.
+ * Remove buckets that start older than requested cutoff.
+ *
+ * This method will remove any bucket that contains any data older than the requested
+ * cutoff, even if that same bucket includes some data from after the cutoff.
+ *
* @hide
*/
- public void removeBucketsBefore(long cutoff) {
+ public void removeBucketsStartingBefore(final long cutoff) {
// TODO: Consider use getIndexBefore.
int i;
for (i = 0; i < bucketCount; i++) {
final long curStart = bucketStart[i];
- final long curEnd = curStart + bucketDuration;
- // cutoff happens before or during this bucket; everything before
- // this bucket should be removed.
- if (curEnd > cutoff) break;
+ // This bucket starts after or at the cutoff, so it should be kept.
+ if (curStart >= cutoff) break;
}
if (i > 0) {
diff --git a/framework/src/android/net/ProfileNetworkPreference.java b/framework/src/android/net/ProfileNetworkPreference.java
index fb271e3..fdcab02 100644
--- a/framework/src/android/net/ProfileNetworkPreference.java
+++ b/framework/src/android/net/ProfileNetworkPreference.java
@@ -120,8 +120,8 @@
public String toString() {
return "ProfileNetworkPreference{"
+ "mPreference=" + getPreference()
- + "mIncludedUids=" + mIncludedUids.toString()
- + "mExcludedUids=" + mExcludedUids.toString()
+ + "mIncludedUids=" + Arrays.toString(mIncludedUids)
+ + "mExcludedUids=" + Arrays.toString(mExcludedUids)
+ "mPreferenceEnterpriseId=" + mPreferenceEnterpriseId
+ '}';
}
diff --git a/service-t/src/com/android/server/NsdService.java b/service-t/src/com/android/server/NsdService.java
index 4086e4e..ea57bac 100644
--- a/service-t/src/com/android/server/NsdService.java
+++ b/service-t/src/com/android/server/NsdService.java
@@ -738,7 +738,13 @@
String type = service.getServiceType();
int port = service.getPort();
byte[] textRecord = service.getTxtRecord();
- return mMDnsManager.registerService(regId, name, type, port, textRecord, IFACE_IDX_ANY);
+ final Network network = service.getNetwork();
+ final int registerInterface = getNetworkInterfaceIndex(network);
+ if (network != null && registerInterface == IFACE_IDX_ANY) {
+ Log.e(TAG, "Interface to register service on not found");
+ return false;
+ }
+ return mMDnsManager.registerService(regId, name, type, port, textRecord, registerInterface);
}
private boolean unregisterService(int regId) {
diff --git a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
index fe27335..09782fd 100644
--- a/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
+++ b/service-t/src/com/android/server/ethernet/EthernetNetworkFactory.java
@@ -194,7 +194,7 @@
}
final NetworkInterfaceState iface = new NetworkInterfaceState(
- ifaceName, hwAddress, mHandler, mContext, ipConfig, nc, this, mDeps);
+ ifaceName, hwAddress, mHandler, mContext, ipConfig, nc, getProvider(), mDeps);
mTrackingInterfaces.put(ifaceName, iface);
updateCapabilityFilter();
}
@@ -361,7 +361,7 @@
private final String mHwAddress;
private final Handler mHandler;
private final Context mContext;
- private final NetworkFactory mNetworkFactory;
+ private final NetworkProvider mNetworkProvider;
private final Dependencies mDeps;
private static String sTcpBufferSizes = null; // Lazy initialized.
@@ -471,14 +471,14 @@
NetworkInterfaceState(String ifaceName, String hwAddress, Handler handler, Context context,
@NonNull IpConfiguration ipConfig, @NonNull NetworkCapabilities capabilities,
- NetworkFactory networkFactory, Dependencies deps) {
+ NetworkProvider networkProvider, Dependencies deps) {
name = ifaceName;
mIpConfig = Objects.requireNonNull(ipConfig);
mCapabilities = Objects.requireNonNull(capabilities);
mLegacyType = getLegacyType(mCapabilities);
mHandler = handler;
mContext = context;
- mNetworkFactory = networkFactory;
+ mNetworkProvider = networkProvider;
mDeps = deps;
mHwAddress = hwAddress;
}
@@ -575,7 +575,7 @@
.setLegacyExtraInfo(mHwAddress)
.build();
mNetworkAgent = mDeps.makeEthernetNetworkAgent(mContext, mHandler.getLooper(),
- mCapabilities, mLinkProperties, config, mNetworkFactory.getProvider(),
+ mCapabilities, mLinkProperties, config, mNetworkProvider,
new EthernetNetworkAgent.Callbacks() {
@Override
public void onNetworkUnwanted() {
diff --git a/service-t/src/com/android/server/net/NetworkStatsObservers.java b/service-t/src/com/android/server/net/NetworkStatsObservers.java
index d974a3b..df4e7f5 100644
--- a/service-t/src/com/android/server/net/NetworkStatsObservers.java
+++ b/service-t/src/com/android/server/net/NetworkStatsObservers.java
@@ -44,6 +44,7 @@
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.PerUidCounter;
import java.util.concurrent.atomic.AtomicInteger;
@@ -61,10 +62,19 @@
private static final int MSG_UNREGISTER = 2;
private static final int MSG_UPDATE_STATS = 3;
+ private static final int DUMP_USAGE_REQUESTS_COUNT = 200;
+
+ // The maximum number of request allowed per uid before an exception is thrown.
+ @VisibleForTesting
+ static final int MAX_REQUESTS_PER_UID = 100;
+
// All access to this map must be done from the handler thread.
// indexed by DataUsageRequest#requestId
private final SparseArray<RequestInfo> mDataUsageRequests = new SparseArray<>();
+ // Request counters per uid, this is thread safe.
+ private final PerUidCounter mDataUsageRequestsPerUid = new PerUidCounter(MAX_REQUESTS_PER_UID);
+
// Sequence number of DataUsageRequests
private final AtomicInteger mNextDataUsageRequestId = new AtomicInteger();
@@ -87,8 +97,9 @@
DataUsageRequest request = buildRequest(context, inputRequest, callingUid);
RequestInfo requestInfo = buildRequestInfo(request, callback, callingPid, callingUid,
callingPackage, accessLevel);
-
if (LOG) Log.d(TAG, "Registering observer for " + requestInfo);
+ mDataUsageRequestsPerUid.incrementCountOrThrow(callingUid);
+
getHandler().sendMessage(mHandler.obtainMessage(MSG_REGISTER, requestInfo));
return request;
}
@@ -187,6 +198,7 @@
if (LOG) Log.d(TAG, "Unregistering " + requestInfo);
mDataUsageRequests.remove(request.requestId);
+ mDataUsageRequestsPerUid.decrementCountOrThrow(callingUid);
requestInfo.unlinkDeathRecipient();
requestInfo.callCallback(NetworkStatsManager.CALLBACK_RELEASED);
}
@@ -463,7 +475,7 @@
}
public void dump(IndentingPrintWriter pw) {
- for (int i = 0; i < mDataUsageRequests.size(); i++) {
+ for (int i = 0; i < Math.min(mDataUsageRequests.size(), DUMP_USAGE_REQUESTS_COUNT); i++) {
pw.println(mDataUsageRequests.valueAt(i));
}
}
diff --git a/service-t/src/com/android/server/net/NetworkStatsRecorder.java b/service-t/src/com/android/server/net/NetworkStatsRecorder.java
index f62765d..6f070d7 100644
--- a/service-t/src/com/android/server/net/NetworkStatsRecorder.java
+++ b/service-t/src/com/android/server/net/NetworkStatsRecorder.java
@@ -455,6 +455,73 @@
}
}
+ /**
+ * Rewriter that will remove any histories or persisted data points before the
+ * specified cutoff time, only writing data back when modified.
+ */
+ public static class RemoveDataBeforeRewriter implements FileRotator.Rewriter {
+ private final NetworkStatsCollection mTemp;
+ private final long mCutoffMills;
+
+ public RemoveDataBeforeRewriter(long bucketDuration, long cutoffMills) {
+ mTemp = new NetworkStatsCollection(bucketDuration);
+ mCutoffMills = cutoffMills;
+ }
+
+ @Override
+ public void reset() {
+ mTemp.reset();
+ }
+
+ @Override
+ public void read(InputStream in) throws IOException {
+ mTemp.read(in);
+ mTemp.clearDirty();
+ mTemp.removeHistoryBefore(mCutoffMills);
+ }
+
+ @Override
+ public boolean shouldWrite() {
+ return mTemp.isDirty();
+ }
+
+ @Override
+ public void write(OutputStream out) throws IOException {
+ mTemp.write(out);
+ }
+ }
+
+ /**
+ * Remove persisted data which contains or is before the cutoff timestamp.
+ */
+ public void removeDataBefore(long cutoffMillis) throws IOException {
+ if (mRotator != null) {
+ try {
+ mRotator.rewriteAll(new RemoveDataBeforeRewriter(
+ mBucketDuration, cutoffMillis));
+ } catch (IOException e) {
+ Log.wtf(TAG, "problem importing netstats", e);
+ recoverFromWtf();
+ } catch (OutOfMemoryError e) {
+ Log.wtf(TAG, "problem importing netstats", e);
+ recoverFromWtf();
+ }
+ }
+
+ // Clean up any pending stats
+ if (mPending != null) {
+ mPending.removeHistoryBefore(cutoffMillis);
+ }
+ if (mSinceBoot != null) {
+ mSinceBoot.removeHistoryBefore(cutoffMillis);
+ }
+
+ final NetworkStatsCollection complete = mComplete != null ? mComplete.get() : null;
+ if (complete != null) {
+ complete.removeHistoryBefore(cutoffMillis);
+ }
+ }
+
public void dumpLocked(IndentingPrintWriter pw, boolean fullHistory) {
if (mPending != null) {
pw.print("Pending bytes: "); pw.println(mPending.getTotalBytes());
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 7c167ed..b2d8b5e 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -377,8 +377,18 @@
private long mLastStatsSessionPoll;
- /** Map from key {@code OpenSessionKey} to count of opened sessions */
- @GuardedBy("mOpenSessionCallsPerCaller")
+ private final Object mOpenSessionCallsLock = new Object();
+ /**
+ * Map from UID to number of opened sessions. This is used for rate-limt an app to open
+ * session frequently
+ */
+ @GuardedBy("mOpenSessionCallsLock")
+ private final SparseIntArray mOpenSessionCallsPerUid = new SparseIntArray();
+ /**
+ * Map from key {@code OpenSessionKey} to count of opened sessions. This is for recording
+ * the caller of open session and it is only for debugging.
+ */
+ @GuardedBy("mOpenSessionCallsLock")
private final HashMap<OpenSessionKey, Integer> mOpenSessionCallsPerCaller = new HashMap<>();
private final static int DUMP_STATS_SESSION_COUNT = 20;
@@ -415,23 +425,20 @@
* the caller.
*/
private static class OpenSessionKey {
- public final int mPid;
- public final int mUid;
- public final String mPackage;
+ public final int uid;
+ public final String packageName;
- OpenSessionKey(int pid, int uid, @NonNull String packageName) {
- mPid = pid;
- mUid = uid;
- mPackage = packageName;
+ OpenSessionKey(int uid, @NonNull String packageName) {
+ this.uid = uid;
+ this.packageName = packageName;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("{");
- sb.append("pid=").append(mPid).append(",");
- sb.append("uid=").append(mUid).append(",");
- sb.append("package=").append(mPackage);
+ sb.append("uid=").append(uid).append(",");
+ sb.append("package=").append(packageName);
sb.append("}");
return sb.toString();
}
@@ -442,13 +449,12 @@
if (o.getClass() != getClass()) return false;
final OpenSessionKey key = (OpenSessionKey) o;
- return mPid == key.mPid && mUid == key.mUid
- && TextUtils.equals(mPackage, key.mPackage);
+ return this.uid == key.uid && TextUtils.equals(this.packageName, key.packageName);
}
@Override
public int hashCode() {
- return Objects.hash(mPid, mUid, mPackage);
+ return Objects.hash(uid, packageName);
}
}
@@ -843,21 +849,27 @@
final long lastCallTime;
final long now = SystemClock.elapsedRealtime();
- synchronized (mOpenSessionCallsPerCaller) {
- Integer calls = mOpenSessionCallsPerCaller.get(key);
- if (calls == null) {
+ synchronized (mOpenSessionCallsLock) {
+ Integer callsPerCaller = mOpenSessionCallsPerCaller.get(key);
+ if (callsPerCaller == null) {
mOpenSessionCallsPerCaller.put((key), 1);
} else {
- mOpenSessionCallsPerCaller.put(key, Integer.sum(calls, 1));
+ mOpenSessionCallsPerCaller.put(key, Integer.sum(callsPerCaller, 1));
}
+
+ int callsPerUid = mOpenSessionCallsPerUid.get(key.uid, 0);
+ mOpenSessionCallsPerUid.put(key.uid, callsPerUid + 1);
+
+ if (key.uid == android.os.Process.SYSTEM_UID) {
+ return false;
+ }
+
+ // To avoid a non-system user to be rate-limited after system users open sessions,
+ // so update mLastStatsSessionPoll after checked if the uid is SYSTEM_UID.
lastCallTime = mLastStatsSessionPoll;
mLastStatsSessionPoll = now;
}
- if (key.mUid == android.os.Process.SYSTEM_UID) {
- return false;
- }
-
return now - lastCallTime < POLL_RATE_LIMIT_MS;
}
@@ -870,9 +882,8 @@
flags |= NetworkStatsManager.FLAG_POLL_ON_OPEN;
}
// Non-system uids are rate limited for POLL_ON_OPEN.
- final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
- final OpenSessionKey key = new OpenSessionKey(callingPid, callingUid, callingPackage);
+ final OpenSessionKey key = new OpenSessionKey(callingUid, callingPackage);
flags = isRateLimitedForPoll(key)
? flags & (~NetworkStatsManager.FLAG_POLL_ON_OPEN)
: flags;
@@ -1990,6 +2001,9 @@
for (int uid : uids) {
deleteKernelTagData(uid);
}
+
+ // TODO: Remove the UID's entries from mOpenSessionCallsPerUid and
+ // mOpenSessionCallsPerCaller
}
/**
@@ -2114,7 +2128,7 @@
// Get the top openSession callers
final HashMap calls;
- synchronized (mOpenSessionCallsPerCaller) {
+ synchronized (mOpenSessionCallsLock) {
calls = new HashMap<>(mOpenSessionCallsPerCaller);
}
final List<Map.Entry<OpenSessionKey, Integer>> list = new ArrayList<>(calls.entrySet());
diff --git a/service/ServiceConnectivityResources/Android.bp b/service/ServiceConnectivityResources/Android.bp
index f491cc7..02b2875 100644
--- a/service/ServiceConnectivityResources/Android.bp
+++ b/service/ServiceConnectivityResources/Android.bp
@@ -23,6 +23,7 @@
name: "ServiceConnectivityResources",
sdk_version: "module_30",
min_sdk_version: "30",
+ target_sdk_version: "33",
resource_dirs: [
"res",
],
diff --git a/service/native/TrafficController.cpp b/service/native/TrafficController.cpp
index 3e98edb..473c9e3 100644
--- a/service/native/TrafficController.cpp
+++ b/service/native/TrafficController.cpp
@@ -88,7 +88,7 @@
} \
} while (0)
-const std::string uidMatchTypeToString(uint8_t match) {
+const std::string uidMatchTypeToString(uint32_t match) {
std::string matchType;
FLAG_MSG_TRANS(matchType, HAPPY_BOX_MATCH, match);
FLAG_MSG_TRANS(matchType, PENALTY_BOX_MATCH, match);
@@ -272,7 +272,7 @@
if (oldMatch.ok()) {
UidOwnerValue newMatch = {
.iif = (match == IIF_MATCH) ? 0 : oldMatch.value().iif,
- .rule = static_cast<uint8_t>(oldMatch.value().rule & ~match),
+ .rule = oldMatch.value().rule & ~match,
};
if (newMatch.rule == 0) {
RETURN_IF_NOT_OK(mUidOwnerMap.deleteValue(uid));
@@ -296,13 +296,13 @@
if (oldMatch.ok()) {
UidOwnerValue newMatch = {
.iif = iif ? iif : oldMatch.value().iif,
- .rule = static_cast<uint8_t>(oldMatch.value().rule | match),
+ .rule = oldMatch.value().rule | match,
};
RETURN_IF_NOT_OK(mUidOwnerMap.writeValue(uid, newMatch, BPF_ANY));
} else {
UidOwnerValue newMatch = {
.iif = iif,
- .rule = static_cast<uint8_t>(match),
+ .rule = match,
};
RETURN_IF_NOT_OK(mUidOwnerMap.writeValue(uid, newMatch, BPF_ANY));
}
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 1bd5a1d..66ef9e9 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -610,13 +610,6 @@
// Handle private DNS validation status updates.
private static final int EVENT_PRIVATE_DNS_VALIDATION_UPDATE = 38;
- /**
- * used to remove a network request, either a listener or a real request and call unavailable
- * arg1 = UID of caller
- * obj = NetworkRequest
- */
- private static final int EVENT_RELEASE_NETWORK_REQUEST_AND_CALL_UNAVAILABLE = 39;
-
/**
* Event for NetworkMonitor/NetworkAgentInfo to inform ConnectivityService that the network has
* been tested.
@@ -1194,6 +1187,7 @@
/**
* Keeps track of the number of requests made under different uids.
*/
+ // TODO: Remove the hack and use com.android.net.module.util.PerUidCounter instead.
public static class PerUidCounter {
private final int mMaxCountPerUid;
@@ -2628,7 +2622,7 @@
verifyCallingUidAndPackage(callingPackageName, mDeps.getCallingUid());
enforceChangePermission(callingPackageName, callingAttributionTag);
if (mProtectedNetworks.contains(networkType)) {
- enforceConnectivityRestrictedNetworksPermission();
+ enforceConnectivityRestrictedNetworksPermission(true /* checkUidsAllowedList */);
}
InetAddress addr;
@@ -2982,18 +2976,35 @@
android.Manifest.permission.NETWORK_SETTINGS);
}
- private void enforceConnectivityRestrictedNetworksPermission() {
- try {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS,
- "ConnectivityService");
- return;
- } catch (SecurityException e) { /* fallback to ConnectivityInternalPermission */ }
- // TODO: Remove this fallback check after all apps have declared
- // CONNECTIVITY_USE_RESTRICTED_NETWORKS.
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CONNECTIVITY_INTERNAL,
- "ConnectivityService");
+ private boolean checkConnectivityRestrictedNetworksPermission(int callingUid,
+ boolean checkUidsAllowedList) {
+ if (PermissionUtils.checkAnyPermissionOf(mContext,
+ android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS)) {
+ return true;
+ }
+
+ // fallback to ConnectivityInternalPermission
+ // TODO: Remove this fallback check after all apps have declared
+ // CONNECTIVITY_USE_RESTRICTED_NETWORKS.
+ if (PermissionUtils.checkAnyPermissionOf(mContext,
+ android.Manifest.permission.CONNECTIVITY_INTERNAL)) {
+ return true;
+ }
+
+ // Check whether uid is in allowed on restricted networks list.
+ if (checkUidsAllowedList
+ && mPermissionMonitor.isUidAllowedOnRestrictedNetworks(callingUid)) {
+ return true;
+ }
+ return false;
+ }
+
+ private void enforceConnectivityRestrictedNetworksPermission(boolean checkUidsAllowedList) {
+ final int callingUid = mDeps.getCallingUid();
+ if (!checkConnectivityRestrictedNetworksPermission(callingUid, checkUidsAllowedList)) {
+ throw new SecurityException("ConnectivityService: user " + callingUid
+ + " has no permission to access restricted network.");
+ }
}
private void enforceKeepalivePermission() {
@@ -4495,7 +4506,7 @@
private boolean hasCarrierPrivilegeForNetworkCaps(final int callingUid,
@NonNull final NetworkCapabilities caps) {
- if (SdkLevel.isAtLeastT() && mCarrierPrivilegeAuthenticator != null) {
+ if (mCarrierPrivilegeAuthenticator != null) {
return mCarrierPrivilegeAuthenticator.hasCarrierPrivilegeForNetworkCapabilities(
callingUid, caps);
}
@@ -4525,7 +4536,6 @@
private void handleRegisterNetworkRequests(@NonNull final Set<NetworkRequestInfo> nris) {
ensureRunningOnConnectivityServiceThread();
- NetworkRequest requestToBeReleased = null;
for (final NetworkRequestInfo nri : nris) {
mNetworkRequestInfoLogs.log("REGISTER " + nri);
checkNrisConsistency(nri);
@@ -4540,13 +4550,6 @@
}
}
}
- if (req.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
- if (!hasCarrierPrivilegeForNetworkCaps(nri.mUid, req.networkCapabilities)
- && !checkConnectivityRestrictedNetworksPermission(
- nri.mPid, nri.mUid)) {
- requestToBeReleased = req;
- }
- }
}
// If this NRI has a satisfier already, it is replacing an older request that
@@ -4558,11 +4561,6 @@
}
}
- if (requestToBeReleased != null) {
- releaseNetworkRequestAndCallOnUnavailable(requestToBeReleased);
- return;
- }
-
if (mFlags.noRematchAllRequestsOnRegister()) {
rematchNetworksAndRequests(nris);
} else {
@@ -5402,11 +5400,6 @@
/* callOnUnavailable */ false);
break;
}
- case EVENT_RELEASE_NETWORK_REQUEST_AND_CALL_UNAVAILABLE: {
- handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.arg1,
- /* callOnUnavailable */ true);
- break;
- }
case EVENT_SET_ACCEPT_UNVALIDATED: {
Network network = (Network) msg.obj;
handleSetAcceptUnvalidated(network, toBool(msg.arg1), toBool(msg.arg2));
@@ -6625,7 +6618,7 @@
case REQUEST:
networkCapabilities = new NetworkCapabilities(networkCapabilities);
enforceNetworkRequestPermissions(networkCapabilities, callingPackageName,
- callingAttributionTag);
+ callingAttributionTag, callingUid);
// TODO: this is incorrect. We mark the request as metered or not depending on
// the state of the app when the request is filed, but we never change the
// request if the app changes network state. http://b/29964605
@@ -6715,26 +6708,19 @@
}
private void enforceNetworkRequestPermissions(NetworkCapabilities networkCapabilities,
- String callingPackageName, String callingAttributionTag) {
+ String callingPackageName, String callingAttributionTag, final int callingUid) {
if (networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED) == false) {
- if (!networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)) {
- enforceConnectivityRestrictedNetworksPermission();
+ // For T+ devices, callers with carrier privilege could request with CBS capabilities.
+ if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CBS)
+ && hasCarrierPrivilegeForNetworkCaps(callingUid, networkCapabilities)) {
+ return;
}
+ enforceConnectivityRestrictedNetworksPermission(true /* checkUidsAllowedList */);
} else {
enforceChangePermission(callingPackageName, callingAttributionTag);
}
}
- private boolean checkConnectivityRestrictedNetworksPermission(int callerPid, int callerUid) {
- if (checkAnyPermissionOf(callerPid, callerUid,
- android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS)
- || checkAnyPermissionOf(callerPid, callerUid,
- android.Manifest.permission.CONNECTIVITY_INTERNAL)) {
- return true;
- }
- return false;
- }
-
@Override
public boolean requestBandwidthUpdate(Network network) {
enforceAccessPermission();
@@ -6793,7 +6779,7 @@
final int callingUid = mDeps.getCallingUid();
networkCapabilities = new NetworkCapabilities(networkCapabilities);
enforceNetworkRequestPermissions(networkCapabilities, callingPackageName,
- callingAttributionTag);
+ callingAttributionTag, callingUid);
enforceMeteredApnPolicy(networkCapabilities);
ensureRequestableCapabilities(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
@@ -6916,13 +6902,6 @@
EVENT_RELEASE_NETWORK_REQUEST, mDeps.getCallingUid(), 0, networkRequest));
}
- private void releaseNetworkRequestAndCallOnUnavailable(NetworkRequest networkRequest) {
- ensureNetworkRequestHasType(networkRequest);
- mHandler.sendMessage(mHandler.obtainMessage(
- EVENT_RELEASE_NETWORK_REQUEST_AND_CALL_UNAVAILABLE, mDeps.getCallingUid(), 0,
- networkRequest));
- }
-
private void handleRegisterNetworkProvider(NetworkProviderInfo npi) {
if (mNetworkProviderInfos.containsKey(npi.messenger)) {
// Avoid creating duplicates. even if an app makes a direct AIDL call.
@@ -10625,7 +10604,11 @@
if (callback == null) throw new IllegalArgumentException("callback must be non-null");
if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
- enforceConnectivityRestrictedNetworksPermission();
+ // TODO: Check allowed list here and ensure that either a) any QoS callback registered
+ // on this network is unregistered when the app loses permission or b) no QoS
+ // callbacks are sent for restricted networks unless the app currently has permission
+ // to access restricted networks.
+ enforceConnectivityRestrictedNetworksPermission(false /* checkUidsAllowedList */);
}
mQosCallbackTracker.registerCallback(callback, filter, nai);
}
@@ -10677,7 +10660,10 @@
Objects.requireNonNull(profile);
if (preferences.size() == 0) {
- preferences.add((new ProfileNetworkPreference.Builder()).build());
+ final ProfileNetworkPreference pref = new ProfileNetworkPreference.Builder()
+ .setPreference(ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT)
+ .build();
+ preferences.add(pref);
}
PermissionUtils.enforceNetworkStackPermission(mContext);
@@ -10695,12 +10681,14 @@
final List<ProfileNetworkPreferenceList.Preference> preferenceList =
new ArrayList<ProfileNetworkPreferenceList.Preference>();
- boolean allowFallback = true;
+ boolean hasDefaultPreference = false;
for (final ProfileNetworkPreference preference : preferences) {
final NetworkCapabilities nc;
+ boolean allowFallback = true;
switch (preference.getPreference()) {
case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_DEFAULT:
nc = null;
+ hasDefaultPreference = true;
if (preference.getPreferenceEnterpriseId() != 0) {
throw new IllegalArgumentException(
"Invalid enterprise identifier in setProfileNetworkPreferences");
@@ -10710,6 +10698,14 @@
allowFallback = false;
// continue to process the enterprise preference.
case ConnectivityManager.PROFILE_NETWORK_PREFERENCE_ENTERPRISE:
+ // This code is needed even though there is a check later on,
+ // because isRangeAlreadyInPreferenceList assumes that every preference
+ // has a UID list.
+ if (hasDefaultPreference) {
+ throw new IllegalArgumentException(
+ "Default profile preference should not be set along with other "
+ + "preference");
+ }
if (!isEnterpriseIdentifierValid(preference.getPreferenceEnterpriseId())) {
throw new IllegalArgumentException(
"Invalid enterprise identifier in setProfileNetworkPreferences");
@@ -10733,6 +10729,10 @@
}
preferenceList.add(new ProfileNetworkPreferenceList.Preference(
profile, nc, allowFallback));
+ if (hasDefaultPreference && preferenceList.size() > 1) {
+ throw new IllegalArgumentException(
+ "Default profile preference should not be set along with other preference");
+ }
}
mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_PROFILE_NETWORK_PREFERENCE,
new Pair<>(preferenceList, listener)));
@@ -10777,12 +10777,6 @@
return false;
}
- private void validateNetworkCapabilitiesOfProfileNetworkPreference(
- @Nullable final NetworkCapabilities nc) {
- if (null == nc) return; // Null caps are always allowed. It means to remove the setting.
- ensureRequestableCapabilities(nc);
- }
-
private ArraySet<NetworkRequestInfo> createNrisFromProfileNetworkPreferences(
@NonNull final ProfileNetworkPreferenceList prefs) {
final ArraySet<NetworkRequestInfo> result = new ArraySet<>();
@@ -10833,10 +10827,19 @@
private void handleSetProfileNetworkPreference(
@NonNull final List<ProfileNetworkPreferenceList.Preference> preferenceList,
@Nullable final IOnCompleteListener listener) {
+ /*
+ * handleSetProfileNetworkPreference is always called for single user.
+ * preferenceList only contains preferences for different uids within the same user
+ * (enforced by getUidListToBeAppliedForNetworkPreference).
+ * Clear all the existing preferences for the user before applying new preferences.
+ *
+ */
+ mProfileNetworkPreferences = mProfileNetworkPreferences.withoutUser(
+ preferenceList.get(0).user);
for (final ProfileNetworkPreferenceList.Preference preference : preferenceList) {
- validateNetworkCapabilitiesOfProfileNetworkPreference(preference.capabilities);
mProfileNetworkPreferences = mProfileNetworkPreferences.plus(preference);
}
+
removeDefaultNetworkRequestsForPreference(PREFERENCE_ORDER_PROFILE);
addPerAppDefaultNetworkRequests(
createNrisFromProfileNetworkPreferences(mProfileNetworkPreferences));
diff --git a/service/src/com/android/server/connectivity/ClatCoordinator.java b/service/src/com/android/server/connectivity/ClatCoordinator.java
index 3b0b3fd..4a7c77a 100644
--- a/service/src/com/android/server/connectivity/ClatCoordinator.java
+++ b/service/src/com/android/server/connectivity/ClatCoordinator.java
@@ -116,12 +116,10 @@
private final INetd mNetd;
@NonNull
private final Dependencies mDeps;
- // IBpfMap objects {mIngressMap, mEgressMap} are initialized in #maybeStartBpf and closed in
- // #maybeStopBpf.
@Nullable
- private IBpfMap<ClatIngress6Key, ClatIngress6Value> mIngressMap = null;
+ private final IBpfMap<ClatIngress6Key, ClatIngress6Value> mIngressMap;
@Nullable
- private IBpfMap<ClatEgress4Key, ClatEgress4Value> mEgressMap = null;
+ private final IBpfMap<ClatEgress4Key, ClatEgress4Value> mEgressMap;
@Nullable
private ClatdTracker mClatdTracker = null;
@@ -375,35 +373,12 @@
public ClatCoordinator(@NonNull Dependencies deps) {
mDeps = deps;
mNetd = mDeps.getNetd();
- }
-
- private void closeEgressMap() {
- try {
- mEgressMap.close();
- } catch (Exception e) {
- Log.e(TAG, "Cannot close egress4 map: " + e);
- }
- mEgressMap = null;
- }
-
- private void closeIngressMap() {
- try {
- mIngressMap.close();
- } catch (Exception e) {
- Log.e(TAG, "Cannot close ingress6 map: " + e);
- }
- mIngressMap = null;
+ mIngressMap = mDeps.getBpfIngress6Map();
+ mEgressMap = mDeps.getBpfEgress4Map();
}
private void maybeStartBpf(final ClatdTracker tracker) {
- mEgressMap = mDeps.getBpfEgress4Map();
- if (mEgressMap == null) return;
-
- mIngressMap = mDeps.getBpfIngress6Map();
- if (mIngressMap == null) {
- closeEgressMap();
- return;
- }
+ if (mIngressMap == null || mEgressMap == null) return;
final boolean isEthernet;
try {
@@ -747,13 +722,6 @@
} catch (ErrnoException | IllegalStateException e) {
Log.e(TAG, "Could not delete entry (" + rxKey + "): " + e);
}
-
- // Manual close BPF map file descriptors. Just don't rely on that GC releasing to close
- // the file descriptors even if class BpfMap supports close file descriptor in
- // finalize(). If the interfaces are added and removed quickly, too many unclosed file
- // descriptors may cause unexpected problem.
- closeEgressMap();
- closeIngressMap();
}
/**
@@ -822,7 +790,7 @@
}
/**
- * Dump the cordinator information. Only called when clat is started. See Nat464Xlat#dump.
+ * Dump the cordinator information.
*
* @param pw print writer.
*/
diff --git a/service/src/com/android/server/connectivity/PermissionMonitor.java b/service/src/com/android/server/connectivity/PermissionMonitor.java
index c02d9cf..8d99cb4 100755
--- a/service/src/com/android/server/connectivity/PermissionMonitor.java
+++ b/service/src/com/android/server/connectivity/PermissionMonitor.java
@@ -421,7 +421,14 @@
if (appInfo == null) return false;
// Check whether package's uid is in allowed on restricted networks uid list. If so, this
// uid can have netd system permission.
- return mUidsAllowedOnRestrictedNetworks.contains(appInfo.uid);
+ return isUidAllowedOnRestrictedNetworks(appInfo.uid);
+ }
+
+ /**
+ * Returns whether the given uid is in allowed on restricted networks list.
+ */
+ public synchronized boolean isUidAllowedOnRestrictedNetworks(final int uid) {
+ return mUidsAllowedOnRestrictedNetworks.contains(uid);
}
@VisibleForTesting
diff --git a/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java b/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java
index 71f342d..5bafef9 100644
--- a/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java
+++ b/service/src/com/android/server/connectivity/ProfileNetworkPreferenceList.java
@@ -70,23 +70,33 @@
/**
* Returns a new object consisting of this object plus the passed preference.
*
- * If a preference already exists for the same user, it will be replaced by the passed
- * preference. Passing a Preference object containing a null capabilities object is equivalent
- * to (and indeed, implemented as) removing the preference for this user.
+ * It is not expected that unwanted preference already exists for the same user.
+ * All preferences for the user that were previously configured should be cleared before
+ * adding a new preference.
+ * Passing a Preference object containing a null capabilities object is equivalent
+ * to removing the preference for this user.
*/
public ProfileNetworkPreferenceList plus(@NonNull final Preference pref) {
- final ArrayList<Preference> newPrefs = new ArrayList<>();
- for (final Preference existingPref : preferences) {
- if (!existingPref.user.equals(pref.user)) {
- newPrefs.add(existingPref);
- }
- }
+ final ArrayList<Preference> newPrefs = new ArrayList<>(preferences);
if (null != pref.capabilities) {
newPrefs.add(pref);
}
return new ProfileNetworkPreferenceList(newPrefs);
}
+ /**
+ * Remove all preferences corresponding to a user.
+ */
+ public ProfileNetworkPreferenceList withoutUser(UserHandle user) {
+ final ArrayList<Preference> newPrefs = new ArrayList<>();
+ for (final Preference existingPref : preferences) {
+ if (!existingPref.user.equals(user)) {
+ newPrefs.add(existingPref);
+ }
+ }
+ return new ProfileNetworkPreferenceList(newPrefs);
+ }
+
public boolean isEmpty() {
return preferences.isEmpty();
}
diff --git a/tests/common/java/android/net/LinkPropertiesTest.java b/tests/common/java/android/net/LinkPropertiesTest.java
index 345a78d..b66a979 100644
--- a/tests/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/common/java/android/net/LinkPropertiesTest.java
@@ -1262,13 +1262,14 @@
}
@Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ @EnableCompatChanges({LinkProperties.EXCLUDED_ROUTES})
public void testHasExcludeRoute() {
LinkProperties lp = new LinkProperties();
- lp.setInterfaceName("VPN");
- lp.addRoute(new RouteInfo(new IpPrefix(ADDRV4, 2), RTN_UNICAST));
+ lp.setInterfaceName("tun0");
+ lp.addRoute(new RouteInfo(new IpPrefix(ADDRV4, 24), RTN_UNICAST));
lp.addRoute(new RouteInfo(new IpPrefix(ADDRV6, 0), RTN_UNICAST));
assertFalse(lp.hasExcludeRoute());
- lp.addRoute(new RouteInfo(new IpPrefix(ADDRV6, 2), RTN_THROW));
+ lp.addRoute(new RouteInfo(new IpPrefix(ADDRV6, 32), RTN_THROW));
assertTrue(lp.hasExcludeRoute());
}
diff --git a/tests/cts/net/AndroidTestTemplate.xml b/tests/cts/net/AndroidTestTemplate.xml
index 48a1c79..33f3af5 100644
--- a/tests/cts/net/AndroidTestTemplate.xml
+++ b/tests/cts/net/AndroidTestTemplate.xml
@@ -38,4 +38,20 @@
<option name="hidden-api-checks" value="false" />
<option name="isolated-storage" value="false" />
</test>
+ <!-- When this test is run in a Mainline context (e.g. with `mts-tradefed`), only enable it if
+ one of the Mainline modules below is present on the device used for testing. -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <!-- Tethering Module (internal version). -->
+ <option name="mainline-module-package-name" value="com.google.android.tethering" />
+ <!-- Tethering Module (AOSP version). -->
+ <option name="mainline-module-package-name" value="com.android.tethering" />
+ <!-- NetworkStack Module (internal version). Should always be installed with CaptivePortalLogin. -->
+ <option name="mainline-module-package-name" value="com.google.android.networkstack" />
+ <!-- NetworkStack Module (AOSP version). Should always be installed with CaptivePortalLogin. -->
+ <option name="mainline-module-package-name" value="com.android.networkstack" />
+ <!-- Resolver Module (internal version). -->
+ <option name="mainline-module-package-name" value="com.google.android.resolv" />
+ <!-- Resolver Module (AOSP version). -->
+ <option name="mainline-module-package-name" value="com.android.resolv" />
+ </object>
</configuration>
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index a129108..8f6786f 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -52,6 +52,7 @@
import static android.net.ConnectivityManager.TYPE_PROXY;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
+import static android.net.ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks;
import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
@@ -164,7 +165,6 @@
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
-import android.util.Pair;
import android.util.Range;
import androidx.test.InstrumentationRegistry;
@@ -182,6 +182,7 @@
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRuleKt;
+import com.android.testutils.DeviceInfoUtils;
import com.android.testutils.DumpTestUtils;
import com.android.testutils.RecorderCallback.CallbackEntry;
import com.android.testutils.TestHttpServer;
@@ -1605,51 +1606,7 @@
private static boolean isTcpKeepaliveSupportedByKernel() {
final String kVersionString = VintfRuntimeInfo.getKernelVersion();
- return compareMajorMinorVersion(kVersionString, "4.8") >= 0;
- }
-
- private static Pair<Integer, Integer> getVersionFromString(String version) {
- // Only gets major and minor number of the version string.
- final Pattern versionPattern = Pattern.compile("^(\\d+)(\\.(\\d+))?.*");
- final Matcher m = versionPattern.matcher(version);
- if (m.matches()) {
- final int major = Integer.parseInt(m.group(1));
- final int minor = TextUtils.isEmpty(m.group(3)) ? 0 : Integer.parseInt(m.group(3));
- return new Pair<>(major, minor);
- } else {
- return new Pair<>(0, 0);
- }
- }
-
- // TODO: Move to util class.
- private static int compareMajorMinorVersion(final String s1, final String s2) {
- final Pair<Integer, Integer> v1 = getVersionFromString(s1);
- final Pair<Integer, Integer> v2 = getVersionFromString(s2);
-
- if (v1.first == v2.first) {
- return Integer.compare(v1.second, v2.second);
- } else {
- return Integer.compare(v1.first, v2.first);
- }
- }
-
- /**
- * Verifies that version string compare logic returns expected result for various cases.
- * Note that only major and minor number are compared.
- */
- @Test
- public void testMajorMinorVersionCompare() {
- assertEquals(0, compareMajorMinorVersion("4.8.1", "4.8"));
- assertEquals(1, compareMajorMinorVersion("4.9", "4.8.1"));
- assertEquals(1, compareMajorMinorVersion("5.0", "4.8"));
- assertEquals(1, compareMajorMinorVersion("5", "4.8"));
- assertEquals(0, compareMajorMinorVersion("5", "5.0"));
- assertEquals(1, compareMajorMinorVersion("5-beta1", "4.8"));
- assertEquals(0, compareMajorMinorVersion("4.8.0.0", "4.8"));
- assertEquals(0, compareMajorMinorVersion("4.8-RC1", "4.8"));
- assertEquals(0, compareMajorMinorVersion("4.8", "4.8"));
- assertEquals(-1, compareMajorMinorVersion("3.10", "4.8.0"));
- assertEquals(-1, compareMajorMinorVersion("4.7.10.10", "4.8"));
+ return DeviceInfoUtils.compareMajorMinorVersion(kVersionString, "4.8") >= 0;
}
/**
@@ -3212,7 +3169,7 @@
@AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps")
@Test
public void testUidsAllowedOnRestrictedNetworks() throws Exception {
- assumeTrue(TestUtils.shouldTestSApis());
+ assumeTestSApis();
// TODO (b/175199465): figure out a reasonable permission check for
// setUidsAllowedOnRestrictedNetworks that allows tests but not system-external callers.
@@ -3225,10 +3182,10 @@
// because it has been just installed to device. In case the uid is existed in setting
// mistakenly, try to remove the uid and set correct uids to setting.
originalUidsAllowedOnRestrictedNetworks.remove(uid);
- runWithShellPermissionIdentity(() ->
- ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(
- mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
+ runWithShellPermissionIdentity(() -> setUidsAllowedOnRestrictedNetworks(
+ mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
+ // File a restricted network request with permission first to hold the connection.
final TestableNetworkCallback testNetworkCb = new TestableNetworkCallback();
final NetworkRequest testRequest = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
@@ -3240,6 +3197,19 @@
runWithShellPermissionIdentity(() -> requestNetwork(testRequest, testNetworkCb),
CONNECTIVITY_USE_RESTRICTED_NETWORKS);
+ // File another restricted network request without permission.
+ final TestableNetworkCallback restrictedNetworkCb = new TestableNetworkCallback();
+ final NetworkRequest restrictedRequest = new NetworkRequest.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_TEST)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+ .setNetworkSpecifier(CompatUtil.makeTestNetworkSpecifier(
+ TEST_RESTRICTED_NW_IFACE_NAME))
+ .build();
+ // Uid is not in allowed list and no permissions. Expect that SecurityException will throw.
+ assertThrows(SecurityException.class,
+ () -> mCm.requestNetwork(restrictedRequest, restrictedNetworkCb));
+
final NetworkAgent agent = createRestrictedNetworkAgent(mContext);
final Network network = agent.getNetwork();
@@ -3259,19 +3229,26 @@
final Set<Integer> newUidsAllowedOnRestrictedNetworks =
new ArraySet<>(originalUidsAllowedOnRestrictedNetworks);
newUidsAllowedOnRestrictedNetworks.add(uid);
- runWithShellPermissionIdentity(() ->
- ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(
- mContext, newUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
+ runWithShellPermissionIdentity(() -> setUidsAllowedOnRestrictedNetworks(
+ mContext, newUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
// Wait a while for sending allowed uids on the restricted network to netd.
- // TODD: Have a significant signal to know the uids has been send to netd.
+ // TODD: Have a significant signal to know the uids has been sent to netd.
assertBindSocketToNetworkSuccess(network);
+
+ // Uid is in allowed list. Try file network request again.
+ requestNetwork(restrictedRequest, restrictedNetworkCb);
+ // Verify that the network is restricted.
+ restrictedNetworkCb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED,
+ NETWORK_CALLBACK_TIMEOUT_MS,
+ entry -> network.equals(entry.getNetwork())
+ && (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
+ .hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)));
} finally {
agent.unregister();
// Restore setting.
- runWithShellPermissionIdentity(() ->
- ConnectivitySettingsManager.setUidsAllowedOnRestrictedNetworks(
- mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
+ runWithShellPermissionIdentity(() -> setUidsAllowedOnRestrictedNetworks(
+ mContext, originalUidsAllowedOnRestrictedNetworks), NETWORK_SETTINGS);
}
}
@@ -3295,6 +3272,12 @@
assertTrue(dumpOutput, dumpOutput.contains("BPF map content"));
}
+ private void assumeTestSApis() {
+ // Cannot use @IgnoreUpTo(Build.VERSION_CODES.R) because this test also requires API 31
+ // shims, and @IgnoreUpTo does not check that.
+ assumeTrue(TestUtils.shouldTestSApis());
+ }
+
private void unregisterRegisteredCallbacks() {
for (NetworkCallback callback: mRegisteredCallbacks) {
mCm.unregisterNetworkCallback(callback);
diff --git a/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java b/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
index fb720a7..f86c5cd 100644
--- a/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
@@ -29,8 +29,20 @@
import static android.app.usage.NetworkStats.Bucket.STATE_FOREGROUND;
import static android.app.usage.NetworkStats.Bucket.TAG_NONE;
import static android.app.usage.NetworkStats.Bucket.UID_ALL;
+import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID;
+import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID_TAG;
+import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_XT;
+
+import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
+
+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 android.app.AppOpsManager;
+import android.app.Instrumentation;
import android.app.usage.NetworkStats;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
@@ -40,7 +52,11 @@
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkRequest;
+import android.net.NetworkStatsCollection;
+import android.net.NetworkStatsHistory;
import android.net.TrafficStats;
+import android.net.netstats.NetworkStatsDataMigrationUtils;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
@@ -48,11 +64,23 @@
import android.os.SystemClock;
import android.platform.test.annotations.AppModeFull;
import android.telephony.TelephonyManager;
-import android.test.InstrumentationTestCase;
+import android.text.TextUtils;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
import com.android.compatibility.common.util.ShellIdentityUtils;
import com.android.compatibility.common.util.SystemUtil;
+import com.android.modules.utils.build.SdkLevel;
+import com.android.testutils.ConnectivityModuleTest;
+import com.android.testutils.DevSdkIgnoreRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
import java.io.IOException;
import java.io.InputStream;
@@ -62,8 +90,18 @@
import java.net.UnknownHostException;
import java.text.MessageFormat;
import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
-public class NetworkStatsManagerTest extends InstrumentationTestCase {
+@ConnectivityModuleTest
+@AppModeFull(reason = "instant apps cannot be granted USAGE_STATS")
+@RunWith(AndroidJUnit4.class)
+public class NetworkStatsManagerTest {
+ @Rule
+ public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule(Build.VERSION_CODES.Q);
+
private static final String LOG_TAG = "NetworkStatsManagerTest";
private static final String APPOPS_SET_SHELL_COMMAND = "appops set {0} {1} {2}";
private static final String APPOPS_GET_SHELL_COMMAND = "appops get {0} {1}";
@@ -164,9 +202,11 @@
};
private String mPkg;
+ private Context mContext;
private NetworkStatsManager mNsm;
private ConnectivityManager mCm;
private PackageManager mPm;
+ private Instrumentation mInstrumentation;
private long mStartTime;
private long mEndTime;
@@ -224,44 +264,40 @@
}
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mNsm = (NetworkStatsManager) getInstrumentation().getContext()
- .getSystemService(Context.NETWORK_STATS_SERVICE);
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mNsm = mContext.getSystemService(NetworkStatsManager.class);
mNsm.setPollForce(true);
- mCm = (ConnectivityManager) getInstrumentation().getContext()
- .getSystemService(Context.CONNECTIVITY_SERVICE);
+ mCm = mContext.getSystemService(ConnectivityManager.class);
+ mPm = mContext.getPackageManager();
+ mPkg = mContext.getPackageName();
- mPm = getInstrumentation().getContext().getPackageManager();
-
- mPkg = getInstrumentation().getContext().getPackageName();
-
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
mWriteSettingsMode = getAppOpsMode(AppOpsManager.OPSTR_WRITE_SETTINGS);
setAppOpsMode(AppOpsManager.OPSTR_WRITE_SETTINGS, "allow");
mUsageStatsMode = getAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS);
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
if (mWriteSettingsMode != null) {
setAppOpsMode(AppOpsManager.OPSTR_WRITE_SETTINGS, mWriteSettingsMode);
}
if (mUsageStatsMode != null) {
setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, mUsageStatsMode);
}
- super.tearDown();
}
private void setAppOpsMode(String appop, String mode) throws Exception {
final String command = MessageFormat.format(APPOPS_SET_SHELL_COMMAND, mPkg, appop, mode);
- SystemUtil.runShellCommand(command);
+ SystemUtil.runShellCommand(mInstrumentation, command);
}
private String getAppOpsMode(String appop) throws Exception {
final String command = MessageFormat.format(APPOPS_GET_SHELL_COMMAND, mPkg, appop);
- String result = SystemUtil.runShellCommand(command);
+ String result = SystemUtil.runShellCommand(mInstrumentation, command);
if (result == null) {
Log.w(LOG_TAG, "App op " + appop + " could not be read.");
}
@@ -269,7 +305,7 @@
}
private boolean isInForeground() throws IOException {
- String result = SystemUtil.runShellCommand(getInstrumentation(),
+ String result = SystemUtil.runShellCommand(mInstrumentation,
"cmd activity get-uid-state " + Process.myUid());
return result.contains("FOREGROUND");
}
@@ -366,15 +402,14 @@
private String getSubscriberId(int networkIndex) {
int networkType = mNetworkInterfacesToTest[networkIndex].getNetworkType();
if (ConnectivityManager.TYPE_MOBILE == networkType) {
- TelephonyManager tm = (TelephonyManager) getInstrumentation().getContext()
- .getSystemService(Context.TELEPHONY_SERVICE);
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
return ShellIdentityUtils.invokeMethodWithShellPermissions(tm,
(telephonyManager) -> telephonyManager.getSubscriberId());
}
return "";
}
- @AppModeFull
+ @Test
public void testDeviceSummary() throws Exception {
for (int i = 0; i < mNetworkInterfacesToTest.length; ++i) {
if (!shouldTestThisNetworkType(i, MINUTE / 2)) {
@@ -410,7 +445,7 @@
}
}
- @AppModeFull
+ @Test
public void testUserSummary() throws Exception {
for (int i = 0; i < mNetworkInterfacesToTest.length; ++i) {
if (!shouldTestThisNetworkType(i, MINUTE / 2)) {
@@ -446,7 +481,7 @@
}
}
- @AppModeFull
+ @Test
public void testAppSummary() throws Exception {
for (int i = 0; i < mNetworkInterfacesToTest.length; ++i) {
// Use tolerance value that large enough to make sure stats of at
@@ -522,7 +557,7 @@
}
}
- @AppModeFull
+ @Test
public void testAppDetails() throws Exception {
for (int i = 0; i < mNetworkInterfacesToTest.length; ++i) {
// Relatively large tolerance to accommodate for history bucket size.
@@ -565,7 +600,7 @@
}
}
- @AppModeFull
+ @Test
public void testUidDetails() throws Exception {
for (int i = 0; i < mNetworkInterfacesToTest.length; ++i) {
// Relatively large tolerance to accommodate for history bucket size.
@@ -619,7 +654,7 @@
}
}
- @AppModeFull
+ @Test
public void testTagDetails() throws Exception {
for (int i = 0; i < mNetworkInterfacesToTest.length; ++i) {
// Relatively large tolerance to accommodate for history bucket size.
@@ -726,7 +761,7 @@
bucket.getRxBytes(), bucket.getTxBytes()));
}
- @AppModeFull
+ @Test
public void testUidTagStateDetails() throws Exception {
for (int i = 0; i < mNetworkInterfacesToTest.length; ++i) {
// Relatively large tolerance to accommodate for history bucket size.
@@ -803,7 +838,7 @@
}
}
- @AppModeFull
+ @Test
public void testCallback() throws Exception {
for (int i = 0; i < mNetworkInterfacesToTest.length; ++i) {
// Relatively large tolerance to accommodate for history bucket size.
@@ -825,6 +860,43 @@
// storing files of >2MB in CTS.
mNsm.unregisterUsageCallback(usageCallback);
+
+ // For T- devices, the registerUsageCallback invocation below will need a looper
+ // from the thread that calls into the API, which is not available in the test.
+ if (SdkLevel.isAtLeastT()) {
+ mNsm.registerUsageCallback(mNetworkInterfacesToTest[i].getNetworkType(),
+ getSubscriberId(i), THRESHOLD_BYTES, usageCallback);
+ mNsm.unregisterUsageCallback(usageCallback);
+ }
+ }
+ }
+
+ @DevSdkIgnoreRule.IgnoreUpTo(SC_V2)
+ @Test
+ public void testDataMigrationUtils() throws Exception {
+ final List<String> prefixes = List.of(PREFIX_UID, PREFIX_XT, PREFIX_UID_TAG);
+ for (final String prefix : prefixes) {
+ final long duration = TextUtils.equals(PREFIX_XT, prefix) ? TimeUnit.HOURS.toMillis(1)
+ : TimeUnit.HOURS.toMillis(2);
+
+ final NetworkStatsCollection collection =
+ NetworkStatsDataMigrationUtils.readPlatformCollection(prefix, duration);
+
+ final long now = System.currentTimeMillis();
+ final Set<Map.Entry<NetworkStatsCollection.Key, NetworkStatsHistory>> entries =
+ collection.getEntries().entrySet();
+ for (final Map.Entry<NetworkStatsCollection.Key, NetworkStatsHistory> entry : entries) {
+ for (final NetworkStatsHistory.Entry historyEntry : entry.getValue().getEntries()) {
+ // Verify all value fields are reasonable.
+ assertTrue(historyEntry.getBucketStart() <= now);
+ assertTrue(historyEntry.getActiveTime() <= duration);
+ assertTrue(historyEntry.getRxBytes() >= 0);
+ assertTrue(historyEntry.getRxPackets() >= 0);
+ assertTrue(historyEntry.getTxBytes() >= 0);
+ assertTrue(historyEntry.getTxPackets() >= 0);
+ assertTrue(historyEntry.getOperations() >= 0);
+ }
+ }
}
}
diff --git a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
index b139a9b..7b0451f 100644
--- a/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
+++ b/tests/cts/net/src/android/net/cts/NsdManagerTest.kt
@@ -82,6 +82,7 @@
private const val TAG = "NsdManagerTest"
private const val SERVICE_TYPE = "_nmt._tcp"
private const val TIMEOUT_MS = 2000L
+private const val NO_CALLBACK_TIMEOUT_MS = 200L
private const val DBG = false
private val nsdShim = NsdShimImpl.newInstance()
@@ -136,6 +137,11 @@
nextEvent.javaClass.simpleName)
return nextEvent
}
+
+ inline fun assertNoCallback(timeoutMs: Long = NO_CALLBACK_TIMEOUT_MS) {
+ val cb = nextEvents.poll(timeoutMs)
+ assertNull(cb, "Expected no callback but got $cb")
+ }
}
private class NsdRegistrationRecord : RegistrationListener,
@@ -556,6 +562,55 @@
}
}
+ @Test
+ fun testNsdManager_RegisterOnNetwork() {
+ // This test requires shims supporting T+ APIs (NsdServiceInfo.network)
+ assumeTrue(ConstantsShim.VERSION > SC_V2)
+
+ val si = NsdServiceInfo()
+ si.serviceType = SERVICE_TYPE
+ si.serviceName = this.serviceName
+ si.network = testNetwork1.network
+ si.port = 12345 // Test won't try to connect so port does not matter
+
+ // Register service on testNetwork1
+ val registrationRecord = NsdRegistrationRecord()
+ registerService(registrationRecord, si)
+ val discoveryRecord = NsdDiscoveryRecord()
+ val discoveryRecord2 = NsdDiscoveryRecord()
+ val discoveryRecord3 = NsdDiscoveryRecord()
+
+ tryTest {
+ // Discover service on testNetwork1.
+ nsdShim.discoverServices(nsdManager, SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD,
+ testNetwork1.network, Executor { it.run() }, discoveryRecord)
+ // Expect that service is found on testNetwork1
+ val foundInfo = discoveryRecord.waitForServiceDiscovered(
+ serviceName, testNetwork1.network)
+ assertEquals(testNetwork1.network, nsdShim.getNetwork(foundInfo))
+
+ // Discover service on testNetwork2.
+ nsdShim.discoverServices(nsdManager, SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD,
+ testNetwork2.network, Executor { it.run() }, discoveryRecord2)
+ // Expect that discovery is started then no other callbacks.
+ discoveryRecord2.expectCallback<DiscoveryStarted>()
+ discoveryRecord2.assertNoCallback()
+
+ // Discover service on all networks (not specify any network).
+ nsdShim.discoverServices(nsdManager, SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD,
+ null as Network? /* network */, Executor { it.run() }, discoveryRecord3)
+ // Expect that service is found on testNetwork1
+ val foundInfo3 = discoveryRecord3.waitForServiceDiscovered(
+ serviceName, testNetwork1.network)
+ assertEquals(testNetwork1.network, nsdShim.getNetwork(foundInfo3))
+ } cleanupStep {
+ nsdManager.stopServiceDiscovery(discoveryRecord2)
+ discoveryRecord2.expectCallback<DiscoveryStopped>()
+ } cleanup {
+ nsdManager.unregisterService(registrationRecord)
+ }
+ }
+
/**
* Register a service and return its registration record.
*/
diff --git a/tests/unit/java/android/net/NetworkStatsCollectionTest.java b/tests/unit/java/android/net/NetworkStatsCollectionTest.java
index 0f02850..b518a61 100644
--- a/tests/unit/java/android/net/NetworkStatsCollectionTest.java
+++ b/tests/unit/java/android/net/NetworkStatsCollectionTest.java
@@ -37,12 +37,15 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
+import android.annotation.NonNull;
import android.content.res.Resources;
+import android.net.NetworkStatsCollection.Key;
import android.os.Process;
import android.os.UserHandle;
import android.telephony.SubscriptionPlan;
import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
+import android.util.ArrayMap;
import android.util.RecurrenceRule;
import androidx.test.InstrumentationRegistry;
@@ -73,6 +76,8 @@
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
* Tests for {@link NetworkStatsCollection}.
@@ -531,6 +536,86 @@
assertThrows(ArithmeticException.class, () -> multiplySafeByRational(30, 3, 0));
}
+ private static void assertCollectionEntries(
+ @NonNull Map<Key, NetworkStatsHistory> expectedEntries,
+ @NonNull NetworkStatsCollection collection) {
+ final Map<Key, NetworkStatsHistory> actualEntries = collection.getEntries();
+ assertEquals(expectedEntries.size(), actualEntries.size());
+ for (Key expectedKey : expectedEntries.keySet()) {
+ final NetworkStatsHistory expectedHistory = expectedEntries.get(expectedKey);
+ final NetworkStatsHistory actualHistory = actualEntries.get(expectedKey);
+ assertNotNull(actualHistory);
+ assertEquals(expectedHistory.getEntries(), actualHistory.getEntries());
+ actualEntries.remove(expectedKey);
+ }
+ assertEquals(0, actualEntries.size());
+ }
+
+ @Test
+ public void testRemoveHistoryBefore() {
+ final NetworkIdentity testIdent = new NetworkIdentity.Builder()
+ .setSubscriberId(TEST_IMSI).build();
+ final Key key1 = new Key(Set.of(testIdent), 0, 0, 0);
+ final Key key2 = new Key(Set.of(testIdent), 1, 0, 0);
+ final long bucketDuration = 10;
+
+ // Prepare entries for testing, with different bucket start timestamps.
+ final NetworkStatsHistory.Entry entry1 = new NetworkStatsHistory.Entry(10, 10, 40,
+ 4, 50, 5, 60);
+ final NetworkStatsHistory.Entry entry2 = new NetworkStatsHistory.Entry(20, 10, 3,
+ 41, 7, 1, 0);
+ final NetworkStatsHistory.Entry entry3 = new NetworkStatsHistory.Entry(30, 10, 1,
+ 21, 70, 4, 1);
+
+ NetworkStatsHistory history1 = new NetworkStatsHistory.Builder(10, 5)
+ .addEntry(entry1)
+ .addEntry(entry2)
+ .build();
+ NetworkStatsHistory history2 = new NetworkStatsHistory.Builder(10, 5)
+ .addEntry(entry2)
+ .addEntry(entry3)
+ .build();
+ NetworkStatsCollection collection = new NetworkStatsCollection.Builder(bucketDuration)
+ .addEntry(key1, history1)
+ .addEntry(key2, history2)
+ .build();
+
+ // Verify nothing is removed if the cutoff time is equal to bucketStart.
+ collection.removeHistoryBefore(10);
+ final Map<Key, NetworkStatsHistory> expectedEntries = new ArrayMap<>();
+ expectedEntries.put(key1, history1);
+ expectedEntries.put(key2, history2);
+ assertCollectionEntries(expectedEntries, collection);
+
+ // Verify entry1 will be removed if its bucket start before to cutoff timestamp.
+ collection.removeHistoryBefore(11);
+ history1 = new NetworkStatsHistory.Builder(10, 5)
+ .addEntry(entry2)
+ .build();
+ history2 = new NetworkStatsHistory.Builder(10, 5)
+ .addEntry(entry2)
+ .addEntry(entry3)
+ .build();
+ final Map<Key, NetworkStatsHistory> cutoff1Entries1 = new ArrayMap<>();
+ cutoff1Entries1.put(key1, history1);
+ cutoff1Entries1.put(key2, history2);
+ assertCollectionEntries(cutoff1Entries1, collection);
+
+ // Verify entry2 will be removed if its bucket start covers by cutoff timestamp.
+ collection.removeHistoryBefore(22);
+ history2 = new NetworkStatsHistory.Builder(10, 5)
+ .addEntry(entry3)
+ .build();
+ final Map<Key, NetworkStatsHistory> cutoffEntries2 = new ArrayMap<>();
+ // History1 is not expected since the collection will omit empty entries.
+ cutoffEntries2.put(key2, history2);
+ assertCollectionEntries(cutoffEntries2, collection);
+
+ // Verify all entries will be removed if cutoff timestamp covers all.
+ collection.removeHistoryBefore(Long.MAX_VALUE);
+ assertEquals(0, collection.getEntries().size());
+ }
+
/**
* Copy a {@link Resources#openRawResource(int)} into {@link File} for
* testing purposes.
diff --git a/tests/unit/java/android/net/NetworkStatsHistoryTest.java b/tests/unit/java/android/net/NetworkStatsHistoryTest.java
index c5f8c00..26079a2 100644
--- a/tests/unit/java/android/net/NetworkStatsHistoryTest.java
+++ b/tests/unit/java/android/net/NetworkStatsHistoryTest.java
@@ -270,7 +270,7 @@
}
@Test
- public void testRemove() throws Exception {
+ public void testRemoveStartingBefore() throws Exception {
stats = new NetworkStatsHistory(HOUR_IN_MILLIS);
// record some data across 24 buckets
@@ -278,28 +278,28 @@
assertEquals(24, stats.size());
// try removing invalid data; should be no change
- stats.removeBucketsBefore(0 - DAY_IN_MILLIS);
+ stats.removeBucketsStartingBefore(0 - DAY_IN_MILLIS);
assertEquals(24, stats.size());
// try removing far before buckets; should be no change
- stats.removeBucketsBefore(TEST_START - YEAR_IN_MILLIS);
+ stats.removeBucketsStartingBefore(TEST_START - YEAR_IN_MILLIS);
assertEquals(24, stats.size());
// try removing just moments into first bucket; should be no change
- // since that bucket contains data beyond the cutoff
- stats.removeBucketsBefore(TEST_START + SECOND_IN_MILLIS);
+ // since that bucket doesn't contain data starts before the cutoff
+ stats.removeBucketsStartingBefore(TEST_START);
assertEquals(24, stats.size());
// try removing single bucket
- stats.removeBucketsBefore(TEST_START + HOUR_IN_MILLIS);
+ stats.removeBucketsStartingBefore(TEST_START + HOUR_IN_MILLIS);
assertEquals(23, stats.size());
// try removing multiple buckets
- stats.removeBucketsBefore(TEST_START + (4 * HOUR_IN_MILLIS));
+ stats.removeBucketsStartingBefore(TEST_START + (4 * HOUR_IN_MILLIS));
assertEquals(20, stats.size());
// try removing all buckets
- stats.removeBucketsBefore(TEST_START + YEAR_IN_MILLIS);
+ stats.removeBucketsStartingBefore(TEST_START + YEAR_IN_MILLIS);
assertEquals(0, stats.size());
}
@@ -349,7 +349,7 @@
stats.recordData(start, end, entry);
} else {
// trim something
- stats.removeBucketsBefore(r.nextLong());
+ stats.removeBucketsStartingBefore(r.nextLong());
}
}
assertConsistent(stats);
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 06243ea..3374672 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -104,6 +104,10 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
import static android.net.NetworkCapabilities.NET_CAPABILITY_XCAP;
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_2;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_3;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_4;
+import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_5;
import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS;
import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
@@ -469,6 +473,9 @@
private static final int TEST_APP_ID_2 = 104;
private static final int TEST_WORK_PROFILE_APP_UID_2 =
UserHandle.getUid(TEST_WORK_PROFILE_USER_ID, TEST_APP_ID_2);
+ private static final int TEST_APP_ID_3 = 105;
+ private static final int TEST_APP_ID_4 = 106;
+ private static final int TEST_APP_ID_5 = 107;
private static final String CLAT_PREFIX = "v4-";
private static final String MOBILE_IFNAME = "test_rmnet_data0";
@@ -798,6 +805,32 @@
}
}
+ // This was only added in the T SDK, but this test needs to build against the R+S SDKs, too.
+ private static int toSdkSandboxUid(int appUid) {
+ final int firstSdkSandboxUid = 20000;
+ return appUid + (firstSdkSandboxUid - Process.FIRST_APPLICATION_UID);
+ }
+
+ // This function assumes the UID range for user 0 ([1, 99999])
+ private static UidRangeParcel[] uidRangeParcelsExcludingUids(Integer... excludedUids) {
+ int start = 1;
+ Arrays.sort(excludedUids);
+ List<UidRangeParcel> parcels = new ArrayList<UidRangeParcel>();
+ for (int excludedUid : excludedUids) {
+ if (excludedUid == start) {
+ start++;
+ } else {
+ parcels.add(new UidRangeParcel(start, excludedUid - 1));
+ start = excludedUid + 1;
+ }
+ }
+ if (start <= 99999) {
+ parcels.add(new UidRangeParcel(start, 99999));
+ }
+
+ return parcels.toArray(new UidRangeParcel[0]);
+ }
+
private void waitForIdle() {
HandlerUtils.waitForIdle(mCsHandlerThread, TIMEOUT_MS);
waitForIdle(mCellNetworkAgent, TIMEOUT_MS);
@@ -5873,7 +5906,7 @@
}
/**
- * Validate the callback flow CBS request without carrier privilege.
+ * Validate the service throws if request with CBS but without carrier privilege.
*/
@Test
public void testCBSRequestWithoutCarrierPrivilege() throws Exception {
@@ -5882,10 +5915,8 @@
final TestNetworkCallback networkCallback = new TestNetworkCallback();
mServiceContext.setPermission(CONNECTIVITY_USE_RESTRICTED_NETWORKS, PERMISSION_DENIED);
- // Now file the test request and expect it.
- mCm.requestNetwork(nr, networkCallback);
- networkCallback.expectCallback(CallbackEntry.UNAVAILABLE, (Network) null);
- mCm.unregisterNetworkCallback(networkCallback);
+ // Now file the test request and expect the service throws.
+ assertThrows(SecurityException.class, () -> mCm.requestNetwork(nr, networkCallback));
}
private static class TestKeepaliveCallback extends PacketKeepaliveCallback {
@@ -9011,10 +9042,16 @@
allowList);
waitForIdle();
- UidRangeParcel firstHalf = new UidRangeParcel(1, VPN_UID - 1);
- UidRangeParcel secondHalf = new UidRangeParcel(VPN_UID + 1, 99999);
+ final Set<Integer> excludedUids = new ArraySet<Integer>();
+ excludedUids.add(VPN_UID);
+ if (SdkLevel.isAtLeastT()) {
+ // On T onwards, the corresponding SDK sandbox UID should also be excluded
+ excludedUids.add(toSdkSandboxUid(VPN_UID));
+ }
+ final UidRangeParcel[] uidRangeParcels = uidRangeParcelsExcludingUids(
+ excludedUids.toArray(new Integer[0]));
InOrder inOrder = inOrder(mMockNetd);
- expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf);
+ expectNetworkRejectNonSecureVpn(inOrder, true, uidRangeParcels);
// Connect a network when lockdown is active, expect to see it blocked.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
@@ -9038,7 +9075,7 @@
vpnUidCallback.assertNoCallback();
vpnUidDefaultCallback.assertNoCallback();
vpnDefaultCallbackAsUid.assertNoCallback();
- expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf);
+ expectNetworkRejectNonSecureVpn(inOrder, false, uidRangeParcels);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
@@ -9055,13 +9092,14 @@
vpnUidDefaultCallback.assertNoCallback();
vpnDefaultCallbackAsUid.assertNoCallback();
- // The following requires that the UID of this test package is greater than VPN_UID. This
- // is always true in practice because a plain AOSP build with no apps installed has almost
- // 200 packages installed.
- final UidRangeParcel piece1 = new UidRangeParcel(1, VPN_UID - 1);
- final UidRangeParcel piece2 = new UidRangeParcel(VPN_UID + 1, uid - 1);
- final UidRangeParcel piece3 = new UidRangeParcel(uid + 1, 99999);
- expectNetworkRejectNonSecureVpn(inOrder, true, piece1, piece2, piece3);
+ excludedUids.add(uid);
+ if (SdkLevel.isAtLeastT()) {
+ // On T onwards, the corresponding SDK sandbox UID should also be excluded
+ excludedUids.add(toSdkSandboxUid(uid));
+ }
+ final UidRangeParcel[] uidRangeParcelsAlsoExcludingUs = uidRangeParcelsExcludingUids(
+ excludedUids.toArray(new Integer[0]));
+ expectNetworkRejectNonSecureVpn(inOrder, true, uidRangeParcelsAlsoExcludingUs);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
@@ -9087,12 +9125,12 @@
// Everything should now be blocked.
mVpnManagerService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
waitForIdle();
- expectNetworkRejectNonSecureVpn(inOrder, false, piece1, piece2, piece3);
+ expectNetworkRejectNonSecureVpn(inOrder, false, uidRangeParcelsAlsoExcludingUs);
allowList.clear();
mVpnManagerService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */,
allowList);
waitForIdle();
- expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf);
+ expectNetworkRejectNonSecureVpn(inOrder, true, uidRangeParcels);
defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent);
vpnUidCallback.assertNoCallback();
@@ -14628,7 +14666,7 @@
profileNetworkPreferenceBuilder.setPreference(
PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK);
profileNetworkPreferenceBuilder.setPreferenceEnterpriseId(
- NetworkCapabilities.NET_ENTERPRISE_ID_2);
+ NET_ENTERPRISE_ID_2);
registerDefaultNetworkCallbacks();
testPreferenceForUserNetworkUpDownForGivenPreference(
profileNetworkPreferenceBuilder.build(), true,
@@ -14653,6 +14691,393 @@
}
/**
+ * Make sure per-profile networking preference throws exception when default preference
+ * is set along with enterprise preference.
+ */
+ @Test
+ public void testPreferenceWithInvalidPreferenceDefaultAndEnterpriseTogether()
+ throws Exception {
+ final UserHandle testHandle = setupEnterpriseNetwork();
+ mServiceContext.setWorkProfile(testHandle, true);
+
+ final int testWorkProfileAppUid1 =
+ UserHandle.getUid(testHandle.getIdentifier(), TEST_APP_ID);
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder1 =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder1.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder1.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ profileNetworkPreferenceBuilder1.setIncludedUids(new int[]{testWorkProfileAppUid1});
+
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder2 =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder2.setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT);
+ final TestOnCompleteListener listener = new TestOnCompleteListener();
+ Assert.assertThrows(IllegalArgumentException.class,
+ () -> mCm.setProfileNetworkPreferences(
+ testHandle, List.of(profileNetworkPreferenceBuilder1.build(),
+ profileNetworkPreferenceBuilder2.build()),
+ r -> r.run(), listener));
+ Assert.assertThrows(IllegalArgumentException.class,
+ () -> mCm.setProfileNetworkPreferences(
+ testHandle, List.of(profileNetworkPreferenceBuilder2.build(),
+ profileNetworkPreferenceBuilder1.build()),
+ r -> r.run(), listener));
+ }
+
+ /**
+ * Make sure per profile network preferences behave as expected when two slices with
+ * two different apps within same user profile is configured
+ * Make sure per profile network preferences overrides with latest preference when
+ * same user preference is set twice
+ */
+ @Test
+ public void testSetPreferenceWithOverridingPreference()
+ throws Exception {
+ final InOrder inOrder = inOrder(mMockNetd);
+ final UserHandle testHandle = setupEnterpriseNetwork();
+ mServiceContext.setWorkProfile(testHandle, true);
+ registerDefaultNetworkCallbacks();
+
+ final TestNetworkCallback appCb1 = new TestNetworkCallback();
+ final TestNetworkCallback appCb2 = new TestNetworkCallback();
+ final TestNetworkCallback appCb3 = new TestNetworkCallback();
+
+ final int testWorkProfileAppUid1 =
+ UserHandle.getUid(testHandle.getIdentifier(), TEST_APP_ID);
+ final int testWorkProfileAppUid2 =
+ UserHandle.getUid(testHandle.getIdentifier(), TEST_APP_ID_2);
+ final int testWorkProfileAppUid3 =
+ UserHandle.getUid(testHandle.getIdentifier(), TEST_APP_ID_3);
+
+ registerDefaultNetworkCallbackAsUid(appCb1, testWorkProfileAppUid1);
+ registerDefaultNetworkCallbackAsUid(appCb2, testWorkProfileAppUid2);
+ registerDefaultNetworkCallbackAsUid(appCb3, testWorkProfileAppUid3);
+
+ // Connect both a regular cell agent and an enterprise network first.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+
+ final TestNetworkAgentWrapper workAgent1 = makeEnterpriseNetworkAgent(NET_ENTERPRISE_ID_1);
+ final TestNetworkAgentWrapper workAgent2 = makeEnterpriseNetworkAgent(NET_ENTERPRISE_ID_2);
+ workAgent1.connect(true);
+ workAgent2.connect(true);
+
+ mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+ appCb1.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ appCb2.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ appCb3.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+ verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE));
+ verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ workAgent1.getNetwork().netId, INetd.PERMISSION_SYSTEM));
+ verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ workAgent2.getNetwork().netId, INetd.PERMISSION_SYSTEM));
+
+ final TestOnCompleteListener listener = new TestOnCompleteListener();
+
+ // Set preferences for testHandle to map testWorkProfileAppUid1 to
+ // NET_ENTERPRISE_ID_1 and testWorkProfileAppUid2 to NET_ENTERPRISE_ID_2.
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder1 =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder1.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder1.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ profileNetworkPreferenceBuilder1.setIncludedUids(new int[]{testWorkProfileAppUid1});
+
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder2 =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder2.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder2.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_2);
+ profileNetworkPreferenceBuilder2.setIncludedUids(new int[]{testWorkProfileAppUid2});
+
+ mCm.setProfileNetworkPreferences(testHandle,
+ List.of(profileNetworkPreferenceBuilder1.build(),
+ profileNetworkPreferenceBuilder2.build()),
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+ verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
+ workAgent2.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder2.build()),
+ PREFERENCE_ORDER_PROFILE));
+ verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
+ workAgent1.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder1.build()),
+ PREFERENCE_ORDER_PROFILE));
+
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+ appCb1.expectAvailableCallbacksValidated(workAgent1);
+ appCb2.expectAvailableCallbacksValidated(workAgent2);
+
+ // Set preferences for testHandle to map testWorkProfileAppUid3 to
+ // to NET_ENTERPRISE_ID_1.
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder3 =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder3.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder3.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ profileNetworkPreferenceBuilder3.setIncludedUids(new int[]{testWorkProfileAppUid3});
+
+ mCm.setProfileNetworkPreferences(testHandle,
+ List.of(profileNetworkPreferenceBuilder3.build()),
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+ verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
+ workAgent1.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder3.build()),
+ PREFERENCE_ORDER_PROFILE));
+ verify(mMockNetd).networkRemoveUidRangesParcel(new NativeUidRangeConfig(
+ workAgent2.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder2.build()),
+ PREFERENCE_ORDER_PROFILE));
+ verify(mMockNetd).networkRemoveUidRangesParcel(new NativeUidRangeConfig(
+ workAgent1.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder1.build()),
+ PREFERENCE_ORDER_PROFILE));
+
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+ appCb3.expectAvailableCallbacksValidated(workAgent1);
+ appCb2.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ appCb1.expectAvailableCallbacksValidated(mCellNetworkAgent);
+
+ // Set the preferences for testHandle to default.
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT);
+
+ mCm.setProfileNetworkPreferences(testHandle,
+ List.of(profileNetworkPreferenceBuilder.build()),
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+ verify(mMockNetd).networkRemoveUidRangesParcel(new NativeUidRangeConfig(
+ workAgent1.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder3.build()),
+ PREFERENCE_ORDER_PROFILE));
+
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback, appCb1, appCb2);
+ appCb3.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ workAgent2.disconnect();
+ mCellNetworkAgent.disconnect();
+
+ mCm.unregisterNetworkCallback(appCb1);
+ mCm.unregisterNetworkCallback(appCb2);
+ mCm.unregisterNetworkCallback(appCb3);
+ // Other callbacks will be unregistered by tearDown()
+ }
+
+ /**
+ * Make sure per profile network preferences behave as expected when multiple slices with
+ * multiple different apps within same user profile is configured.
+ */
+ @Test
+ public void testSetPreferenceWithMultiplePreferences()
+ throws Exception {
+ final InOrder inOrder = inOrder(mMockNetd);
+
+ final UserHandle testHandle = setupEnterpriseNetwork();
+ mServiceContext.setWorkProfile(testHandle, true);
+ registerDefaultNetworkCallbacks();
+
+ final TestNetworkCallback appCb1 = new TestNetworkCallback();
+ final TestNetworkCallback appCb2 = new TestNetworkCallback();
+ final TestNetworkCallback appCb3 = new TestNetworkCallback();
+ final TestNetworkCallback appCb4 = new TestNetworkCallback();
+ final TestNetworkCallback appCb5 = new TestNetworkCallback();
+
+ final int testWorkProfileAppUid1 =
+ UserHandle.getUid(testHandle.getIdentifier(), TEST_APP_ID);
+ final int testWorkProfileAppUid2 =
+ UserHandle.getUid(testHandle.getIdentifier(), TEST_APP_ID_2);
+ final int testWorkProfileAppUid3 =
+ UserHandle.getUid(testHandle.getIdentifier(), TEST_APP_ID_3);
+ final int testWorkProfileAppUid4 =
+ UserHandle.getUid(testHandle.getIdentifier(), TEST_APP_ID_4);
+ final int testWorkProfileAppUid5 =
+ UserHandle.getUid(testHandle.getIdentifier(), TEST_APP_ID_5);
+
+ registerDefaultNetworkCallbackAsUid(appCb1, testWorkProfileAppUid1);
+ registerDefaultNetworkCallbackAsUid(appCb2, testWorkProfileAppUid2);
+ registerDefaultNetworkCallbackAsUid(appCb3, testWorkProfileAppUid3);
+ registerDefaultNetworkCallbackAsUid(appCb4, testWorkProfileAppUid4);
+ registerDefaultNetworkCallbackAsUid(appCb5, testWorkProfileAppUid5);
+
+ // Connect both a regular cell agent and an enterprise network first.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+
+ final TestNetworkAgentWrapper workAgent1 = makeEnterpriseNetworkAgent(NET_ENTERPRISE_ID_1);
+ final TestNetworkAgentWrapper workAgent2 = makeEnterpriseNetworkAgent(NET_ENTERPRISE_ID_2);
+ final TestNetworkAgentWrapper workAgent3 = makeEnterpriseNetworkAgent(NET_ENTERPRISE_ID_3);
+ final TestNetworkAgentWrapper workAgent4 = makeEnterpriseNetworkAgent(NET_ENTERPRISE_ID_4);
+ final TestNetworkAgentWrapper workAgent5 = makeEnterpriseNetworkAgent(NET_ENTERPRISE_ID_5);
+
+ workAgent1.connect(true);
+ workAgent2.connect(true);
+ workAgent3.connect(true);
+ workAgent4.connect(true);
+ workAgent5.connect(true);
+
+ mSystemDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ mDefaultNetworkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ appCb1.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ appCb2.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ appCb3.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ appCb4.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ appCb5.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+
+ verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ mCellNetworkAgent.getNetwork().netId, INetd.PERMISSION_NONE));
+ verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ workAgent1.getNetwork().netId, INetd.PERMISSION_SYSTEM));
+ verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ workAgent2.getNetwork().netId, INetd.PERMISSION_SYSTEM));
+ verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ workAgent3.getNetwork().netId, INetd.PERMISSION_SYSTEM));
+ verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ workAgent4.getNetwork().netId, INetd.PERMISSION_SYSTEM));
+ verify(mMockNetd).networkCreate(nativeNetworkConfigPhysical(
+ workAgent5.getNetwork().netId, INetd.PERMISSION_SYSTEM));
+
+ final TestOnCompleteListener listener = new TestOnCompleteListener();
+
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder1 =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder1.setPreference(PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder1.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_1);
+ profileNetworkPreferenceBuilder1.setIncludedUids(new int[]{testWorkProfileAppUid1});
+
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder2 =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder2.setPreference(
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK);
+ profileNetworkPreferenceBuilder2.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_2);
+ profileNetworkPreferenceBuilder2.setIncludedUids(new int[]{testWorkProfileAppUid2});
+
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder3 =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder3.setPreference(
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder3.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_3);
+ profileNetworkPreferenceBuilder3.setIncludedUids(new int[]{testWorkProfileAppUid3});
+
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder4 =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder4.setPreference(
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE_NO_FALLBACK);
+ profileNetworkPreferenceBuilder4.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_4);
+ profileNetworkPreferenceBuilder4.setIncludedUids(new int[]{testWorkProfileAppUid4});
+
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder5 =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder5.setPreference(
+ PROFILE_NETWORK_PREFERENCE_ENTERPRISE);
+ profileNetworkPreferenceBuilder5.setPreferenceEnterpriseId(NET_ENTERPRISE_ID_5);
+ profileNetworkPreferenceBuilder5.setIncludedUids(new int[]{testWorkProfileAppUid5});
+
+ mCm.setProfileNetworkPreferences(testHandle,
+ List.of(profileNetworkPreferenceBuilder1.build(),
+ profileNetworkPreferenceBuilder2.build(),
+ profileNetworkPreferenceBuilder3.build(),
+ profileNetworkPreferenceBuilder4.build(),
+ profileNetworkPreferenceBuilder5.build()),
+ r -> r.run(), listener);
+
+ listener.expectOnComplete();
+
+ verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
+ workAgent1.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder1.build()),
+ PREFERENCE_ORDER_PROFILE));
+ verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
+ workAgent2.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder2.build()),
+ PREFERENCE_ORDER_PROFILE));
+ verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
+ workAgent3.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder3.build()),
+ PREFERENCE_ORDER_PROFILE));
+ verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
+ workAgent4.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder4.build()),
+ PREFERENCE_ORDER_PROFILE));
+ verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
+ workAgent5.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder5.build()),
+ PREFERENCE_ORDER_PROFILE));
+
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback);
+ appCb1.expectAvailableCallbacksValidated(workAgent1);
+ appCb2.expectAvailableCallbacksValidated(workAgent2);
+ appCb3.expectAvailableCallbacksValidated(workAgent3);
+ appCb4.expectAvailableCallbacksValidated(workAgent4);
+ appCb5.expectAvailableCallbacksValidated(workAgent5);
+
+ workAgent1.disconnect();
+ workAgent2.disconnect();
+ workAgent3.disconnect();
+ workAgent4.disconnect();
+ workAgent5.disconnect();
+
+ appCb1.expectCallback(CallbackEntry.LOST, workAgent1);
+ appCb2.expectCallback(CallbackEntry.LOST, workAgent2);
+ appCb3.expectCallback(CallbackEntry.LOST, workAgent3);
+ appCb4.expectCallback(CallbackEntry.LOST, workAgent4);
+ appCb5.expectCallback(CallbackEntry.LOST, workAgent5);
+
+ appCb1.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ appCb2.assertNoCallback();
+ appCb3.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ appCb4.assertNoCallback();
+ appCb5.expectAvailableCallbacksValidated(mCellNetworkAgent);
+
+ verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
+ mCellNetworkAgent.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder1.build()),
+ PREFERENCE_ORDER_PROFILE));
+ verify(mMockNetd, never()).networkAddUidRangesParcel(new NativeUidRangeConfig(
+ mCellNetworkAgent.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder2.build()),
+ PREFERENCE_ORDER_PROFILE));
+ verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
+ mCellNetworkAgent.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder3.build()),
+ PREFERENCE_ORDER_PROFILE));
+ verify(mMockNetd, never()).networkAddUidRangesParcel(new NativeUidRangeConfig(
+ mCellNetworkAgent.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder4.build()),
+ PREFERENCE_ORDER_PROFILE));
+ verify(mMockNetd).networkAddUidRangesParcel(new NativeUidRangeConfig(
+ mCellNetworkAgent.getNetwork().netId,
+ uidRangeFor(testHandle, profileNetworkPreferenceBuilder5.build()),
+ PREFERENCE_ORDER_PROFILE));
+
+ mSystemDefaultNetworkCallback.assertNoCallback();
+ mDefaultNetworkCallback.assertNoCallback();
+
+ // Set the preferences for testHandle to default.
+ ProfileNetworkPreference.Builder profileNetworkPreferenceBuilder =
+ new ProfileNetworkPreference.Builder();
+ profileNetworkPreferenceBuilder.setPreference(PROFILE_NETWORK_PREFERENCE_DEFAULT);
+
+ mCm.setProfileNetworkPreferences(testHandle,
+ List.of(profileNetworkPreferenceBuilder.build()),
+ r -> r.run(), listener);
+ listener.expectOnComplete();
+ assertNoCallbacks(mSystemDefaultNetworkCallback, mDefaultNetworkCallback, appCb1, appCb3,
+ appCb5);
+ appCb2.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ appCb4.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ mCellNetworkAgent.disconnect();
+
+ mCm.unregisterNetworkCallback(appCb1);
+ mCm.unregisterNetworkCallback(appCb2);
+ mCm.unregisterNetworkCallback(appCb3);
+ mCm.unregisterNetworkCallback(appCb4);
+ mCm.unregisterNetworkCallback(appCb5);
+ // Other callbacks will be unregistered by tearDown()
+ }
+
+ /**
* Test that, in a given networking context, calling setPreferenceForUser to set per-profile
* defaults on then off works as expected.
*/
diff --git a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
index a1eeaf4..f84d10f 100644
--- a/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
+++ b/tests/unit/java/com/android/server/connectivity/ClatCoordinatorTest.java
@@ -447,8 +447,6 @@
argThat(fd -> Objects.equals(RAW_SOCK_PFD.getFileDescriptor(), fd)),
eq(BASE_IFACE), eq(NAT64_PREFIX_STRING),
eq(XLAT_LOCAL_IPV4ADDR_STRING), eq(XLAT_LOCAL_IPV6ADDR_STRING));
- inOrder.verify(mDeps).getBpfEgress4Map();
- inOrder.verify(mDeps).getBpfIngress6Map();
inOrder.verify(mEgressMap).insertEntry(eq(EGRESS_KEY), eq(EGRESS_VALUE));
inOrder.verify(mIngressMap).insertEntry(eq(INGRESS_KEY), eq(INGRESS_VALUE));
inOrder.verify(mDeps).tcQdiscAddDevClsact(eq(STACKED_IFINDEX));
@@ -471,8 +469,6 @@
eq((short) PRIO_CLAT), eq((short) ETH_P_IP));
inOrder.verify(mEgressMap).deleteEntry(eq(EGRESS_KEY));
inOrder.verify(mIngressMap).deleteEntry(eq(INGRESS_KEY));
- inOrder.verify(mEgressMap).close();
- inOrder.verify(mIngressMap).close();
inOrder.verify(mDeps).stopClatd(eq(BASE_IFACE), eq(NAT64_PREFIX_STRING),
eq(XLAT_LOCAL_IPV4ADDR_STRING), eq(XLAT_LOCAL_IPV6ADDR_STRING), eq(CLATD_PID));
inOrder.verify(mDeps).untagSocket(eq(RAW_SOCK_COOKIE));
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 9e79162..11fbcb9 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -28,9 +28,11 @@
import static android.net.INetd.IF_STATE_DOWN;
import static android.net.INetd.IF_STATE_UP;
import static android.net.VpnManager.TYPE_VPN_PLATFORM;
+import static android.os.Build.VERSION_CODES.S_V2;
import static android.os.UserHandle.PER_USER_RANGE;
import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
+import static com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import static com.android.testutils.MiscAsserts.assertThrows;
import static org.junit.Assert.assertArrayEquals;
@@ -128,11 +130,13 @@
import com.android.internal.util.HexDump;
import com.android.modules.utils.build.SdkLevel;
import com.android.server.IpSecService;
+import com.android.server.vcn.util.PersistableBundleUtils;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.AdditionalAnswers;
@@ -169,10 +173,13 @@
*/
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(VERSION_CODES.R)
+@IgnoreUpTo(VERSION_CODES.S_V2)
public class VpnTest {
private static final String TAG = "VpnTest";
+ @Rule
+ public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
+
// Mock users
static final UserInfo primaryUser = new UserInfo(27, "Primary", FLAG_ADMIN | FLAG_PRIMARY);
static final UserInfo secondaryUser = new UserInfo(15, "Secondary", FLAG_ADMIN);
@@ -196,28 +203,16 @@
private static final String TEST_IFACE_NAME = "TEST_IFACE";
private static final int TEST_TUNNEL_RESOURCE_ID = 0x2345;
private static final long TEST_TIMEOUT_MS = 500L;
-
+ private static final String PRIMARY_USER_APP_EXCLUDE_KEY =
+ "VPN_APP_EXCLUDED_27_com.testvpn.vpn";
/**
* Names and UIDs for some fake packages. Important points:
* - UID is ordered increasing.
* - One pair of packages have consecutive UIDs.
*/
static final String[] PKGS = {"com.example", "org.example", "net.example", "web.vpn"};
- static final String PKGS_BYTES =
- "3C62756E646C653E0A3C696E74206E616D653D22434F4C4C454354494F4E5F4C454E4754482220"
- + "76616C75653D223422202F3E0A3C7062756E646C655F61735F6D6170206E616D653D224C4953"
- + "545F4954454D5F30223E0A3C737472696E67206E616D653D22535452494E475F4B4559223E63"
- + "6F6D2E6578616D706C653C2F737472696E673E0A3C2F7062756E646C655F61735F6D61703E0A"
- + "3C7062756E646C655F61735F6D6170206E616D653D224C4953545F4954454D5F31223E0A3C73"
- + "7472696E67206E616D653D22535452494E475F4B4559223E6F72672E6578616D706C653C2F73"
- + "7472696E673E0A3C2F7062756E646C655F61735F6D61703E0A3C7062756E646C655F61735F6D"
- + "6170206E616D653D224C4953545F4954454D5F32223E0A3C737472696E67206E616D653D2253"
- + "5452494E475F4B4559223E6E65742E6578616D706C653C2F737472696E673E0A3C2F7062756E"
- + "646C655F61735F6D61703E0A3C7062756E646C655F61735F6D6170206E616D653D224C495354"
- + "5F4954454D5F33223E0A3C737472696E67206E616D653D22535452494E475F4B4559223E7765"
- + "622E76706E3C2F737472696E673E0A3C2F7062756E646C655F61735F6D61703E0A3C2F62756E"
- + "646C653E0A";
- static final int[] PKG_UIDS = {66, 77, 78, 400};
+ static final String PKGS_BYTES = getPackageByteString(List.of(PKGS));
+ static final int[] PKG_UIDS = {10066, 10077, 10078, 10400};
// Mock packages
static final Map<String, Integer> mPackages = new ArrayMap<>();
@@ -330,6 +325,17 @@
return new Range<Integer>(start, stop);
}
+ private static String getPackageByteString(List<String> packages) {
+ try {
+ return HexDump.toHexString(
+ PersistableBundleUtils.toDiskStableBytes(PersistableBundleUtils.fromList(
+ packages, PersistableBundleUtils.STRING_SERIALIZER)),
+ true /* upperCase */);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
@Test
public void testRestrictedProfilesAreAddedToVpn() {
setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB);
@@ -379,7 +385,11 @@
Arrays.asList(packages), null /* disallowedApplications */);
assertEquals(rangeSet(
uidRange(userStart + PKG_UIDS[0], userStart + PKG_UIDS[0]),
- uidRange(userStart + PKG_UIDS[1], userStart + PKG_UIDS[2])),
+ uidRange(userStart + PKG_UIDS[1], userStart + PKG_UIDS[2]),
+ uidRange(Process.toSdkSandboxUid(userStart + PKG_UIDS[0]),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[0])),
+ uidRange(Process.toSdkSandboxUid(userStart + PKG_UIDS[1]),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[2]))),
allow);
// Denied list
@@ -390,7 +400,11 @@
uidRange(userStart, userStart + PKG_UIDS[0] - 1),
uidRange(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[1] - 1),
/* Empty range between UIDS[1] and UIDS[2], should be excluded, */
- uidRange(userStart + PKG_UIDS[2] + 1, userStop)),
+ uidRange(userStart + PKG_UIDS[2] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[0] - 1)),
+ uidRange(Process.toSdkSandboxUid(userStart + PKG_UIDS[0] + 1),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[1] - 1)),
+ uidRange(Process.toSdkSandboxUid(userStart + PKG_UIDS[2] + 1), userStop)),
disallow);
}
@@ -431,18 +445,24 @@
assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null));
verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1),
- new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStop)
+ new UidRangeParcel(userStart + PKG_UIDS[1] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[1] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[1] + 1), userStop)
}));
// Switch to another app.
assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null));
verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1),
- new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStop)
+ new UidRangeParcel(userStart + PKG_UIDS[1] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[1] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[1] + 1), userStop)
}));
verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(userStart, userStart + PKG_UIDS[3] - 1),
- new UidRangeParcel(userStart + PKG_UIDS[3] + 1, userStop)
+ new UidRangeParcel(userStart + PKG_UIDS[3] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[3] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[3] + 1), userStop)
}));
}
@@ -457,17 +477,25 @@
PKGS[1], true, Collections.singletonList(PKGS[2])));
verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1),
- new UidRangeParcel(userStart + PKG_UIDS[2] + 1, userStop)
+ new UidRangeParcel(userStart + PKG_UIDS[2] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[1]) - 1),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[2] + 1), userStop)
}));
// Change allowed app list to PKGS[3].
assertTrue(vpn.setAlwaysOnPackage(
PKGS[1], true, Collections.singletonList(PKGS[3])));
verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
- new UidRangeParcel(userStart + PKG_UIDS[2] + 1, userStop)
+ new UidRangeParcel(userStart + PKG_UIDS[2] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[1] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[2] + 1), userStop)
}));
verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStart + PKG_UIDS[3] - 1),
- new UidRangeParcel(userStart + PKG_UIDS[3] + 1, userStop)
+ new UidRangeParcel(userStart + PKG_UIDS[3] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[1] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[1] + 1),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[3] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[3] + 1), userStop)
}));
// Change the VPN app.
@@ -475,32 +503,52 @@
PKGS[0], true, Collections.singletonList(PKGS[3])));
verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
new UidRangeParcel(userStart, userStart + PKG_UIDS[1] - 1),
- new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStart + PKG_UIDS[3] - 1)
+ new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStart + PKG_UIDS[3] - 1),
+ new UidRangeParcel(userStart + PKG_UIDS[3] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[1] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[1] + 1),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[3] - 1))
}));
verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(userStart, userStart + PKG_UIDS[0] - 1),
- new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[3] - 1)
+ new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[3] - 1),
+ new UidRangeParcel(userStart + PKG_UIDS[3] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[0] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[0] + 1),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[3] - 1))
}));
// Remove the list of allowed packages.
assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null));
verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[3] - 1),
- new UidRangeParcel(userStart + PKG_UIDS[3] + 1, userStop)
+ new UidRangeParcel(userStart + PKG_UIDS[3] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[0] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[0] + 1),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[3] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[3] + 1), userStop)
}));
verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
- new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStop),
+ new UidRangeParcel(userStart + PKG_UIDS[0] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[0] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[0] + 1), userStop),
}));
// Add the list of allowed packages.
assertTrue(vpn.setAlwaysOnPackage(
PKGS[0], true, Collections.singletonList(PKGS[1])));
verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
- new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStop)
+ new UidRangeParcel(userStart + PKG_UIDS[0] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[0] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[0] + 1), userStop),
}));
verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[1] - 1),
- new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStop)
+ new UidRangeParcel(userStart + PKG_UIDS[1] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[0] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[0] + 1),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[1] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[1] + 1), userStop)
}));
// Try allowing a package with a comma, should be rejected.
@@ -513,11 +561,19 @@
PKGS[0], true, Arrays.asList("com.foo.app", PKGS[2], "com.bar.app")));
verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[1] - 1),
- new UidRangeParcel(userStart + PKG_UIDS[1] + 1, userStop)
+ new UidRangeParcel(userStart + PKG_UIDS[1] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[0] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[0] + 1),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[1] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[1] + 1), userStop)
}));
verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
new UidRangeParcel(userStart + PKG_UIDS[0] + 1, userStart + PKG_UIDS[2] - 1),
- new UidRangeParcel(userStart + PKG_UIDS[2] + 1, userStop)
+ new UidRangeParcel(userStart + PKG_UIDS[2] + 1,
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[0] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[0] + 1),
+ Process.toSdkSandboxUid(userStart + PKG_UIDS[2] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(userStart + PKG_UIDS[2] + 1), userStop)
}));
}
@@ -562,7 +618,10 @@
};
final UidRangeParcel[] exceptPkg0 = {
new UidRangeParcel(entireUser[0].start, entireUser[0].start + PKG_UIDS[0] - 1),
- new UidRangeParcel(entireUser[0].start + PKG_UIDS[0] + 1, entireUser[0].stop)
+ new UidRangeParcel(entireUser[0].start + PKG_UIDS[0] + 1,
+ Process.toSdkSandboxUid(entireUser[0].start + PKG_UIDS[0] - 1)),
+ new UidRangeParcel(Process.toSdkSandboxUid(entireUser[0].start + PKG_UIDS[0] + 1),
+ entireUser[0].stop),
};
final InOrder order = inOrder(mConnectivityManager);
@@ -731,11 +790,10 @@
}
private Vpn prepareVpnForVerifyAppExclusionList() throws Exception {
- assumeTrue(isAtLeastT());
final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
.thenReturn(mVpnProfile.encode());
- when(mVpnProfileStore.get(vpn.getVpnAppExcludedForPackage(TEST_VPN_PKG)))
+ when(mVpnProfileStore.get(PRIMARY_USER_APP_EXCLUDE_KEY))
.thenReturn(HexDump.hexStringToByteArray(PKGS_BYTES));
vpn.startVpnProfile(TEST_VPN_PKG);
@@ -747,12 +805,13 @@
return vpn;
}
- @Test
+ @Test @IgnoreUpTo(S_V2)
public void testSetAndGetAppExclusionList() throws Exception {
final Vpn vpn = prepareVpnForVerifyAppExclusionList();
+ verify(mVpnProfileStore, never()).put(eq(PRIMARY_USER_APP_EXCLUDE_KEY), any());
vpn.setAppExclusionList(TEST_VPN_PKG, Arrays.asList(PKGS));
verify(mVpnProfileStore)
- .put(eq(vpn.getVpnAppExcludedForPackage(TEST_VPN_PKG)),
+ .put(eq(PRIMARY_USER_APP_EXCLUDE_KEY),
eq(HexDump.hexStringToByteArray(PKGS_BYTES)));
assertEquals(vpn.createUserAndRestrictedProfilesRanges(
primaryUser.id, null, Arrays.asList(PKGS)),
@@ -760,7 +819,7 @@
assertEquals(Arrays.asList(PKGS), vpn.getAppExclusionList(TEST_VPN_PKG));
}
- @Test
+ @Test @IgnoreUpTo(S_V2)
public void testSetAndGetAppExclusionListRestrictedUser() throws Exception {
final Vpn vpn = prepareVpnForVerifyAppExclusionList();
// Mock it to restricted profile
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
index 13a85e8..e8c9637 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -32,6 +32,7 @@
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
@@ -64,6 +65,7 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
import java.util.Objects;
/**
@@ -185,6 +187,51 @@
}
@Test
+ public void testRegister_limit() throws Exception {
+ final DataUsageRequest inputRequest = new DataUsageRequest(
+ DataUsageRequest.REQUEST_ID_UNSET, sTemplateWifi, THRESHOLD_BYTES);
+
+ // Register maximum requests for red.
+ final ArrayList<DataUsageRequest> redRequests = new ArrayList<>();
+ for (int i = 0; i < NetworkStatsObservers.MAX_REQUESTS_PER_UID; i++) {
+ final DataUsageRequest returnedRequest =
+ mStatsObservers.register(mContext, inputRequest, mUsageCallback,
+ PID_RED, UID_RED, PACKAGE_RED, NetworkStatsAccess.Level.DEVICE);
+ redRequests.add(returnedRequest);
+ assertTrue(returnedRequest.requestId > 0);
+ }
+
+ // Verify request exceeds the limit throws.
+ assertThrows(IllegalStateException.class, () ->
+ mStatsObservers.register(mContext, inputRequest, mUsageCallback,
+ PID_RED, UID_RED, PACKAGE_RED, NetworkStatsAccess.Level.DEVICE));
+
+ // Verify another uid is not affected.
+ final ArrayList<DataUsageRequest> blueRequests = new ArrayList<>();
+ for (int i = 0; i < NetworkStatsObservers.MAX_REQUESTS_PER_UID; i++) {
+ final DataUsageRequest returnedRequest =
+ mStatsObservers.register(mContext, inputRequest, mUsageCallback,
+ PID_BLUE, UID_BLUE, PACKAGE_BLUE, NetworkStatsAccess.Level.DEVICE);
+ blueRequests.add(returnedRequest);
+ assertTrue(returnedRequest.requestId > 0);
+ }
+
+ // Again, verify request exceeds the limit throws for the 2nd uid.
+ assertThrows(IllegalStateException.class, () ->
+ mStatsObservers.register(mContext, inputRequest, mUsageCallback,
+ PID_RED, UID_RED, PACKAGE_RED, NetworkStatsAccess.Level.DEVICE));
+
+ // Unregister all registered requests. Note that exceptions cannot be tested since
+ // unregister is handled in the handler thread.
+ for (final DataUsageRequest request : redRequests) {
+ mStatsObservers.unregister(request, UID_RED);
+ }
+ for (final DataUsageRequest request : blueRequests) {
+ mStatsObservers.unregister(request, UID_BLUE);
+ }
+ }
+
+ @Test
public void testUnregister_unknownRequest_noop() throws Exception {
DataUsageRequest unknownRequest = new DataUsageRequest(
123456 /* id */, sTemplateWifi, THRESHOLD_BYTES);