Merge "[Thread] move infra network utilities to IntegrationTestUtils.kt" into main
diff --git a/Tethering/apex/Android.bp b/Tethering/apex/Android.bp
index 8d96066..3b197fc 100644
--- a/Tethering/apex/Android.bp
+++ b/Tethering/apex/Android.bp
@@ -114,7 +114,7 @@
"current_sdkinfo",
"netbpfload.33rc",
"netbpfload.35rc",
- "ot-daemon.init.34rc",
+ "ot-daemon.34rc",
],
manifest: "manifest.json",
key: "com.android.tethering.key",
diff --git a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
index 4d1e7ef..e6e99f4 100644
--- a/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
+++ b/Tethering/apishim/31/com/android/networkstack/tethering/apishim/api31/BpfCoordinatorShimImpl.java
@@ -359,6 +359,7 @@
} catch (IllegalStateException e) {
// Silent if the rule already exists. Note that the errno EEXIST was rethrown as
// IllegalStateException. See BpfMap#insertEntry.
+ return false;
}
return true;
}
diff --git a/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java b/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
index d28a397..026b1c3 100644
--- a/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
+++ b/Tethering/apishim/common/com/android/networkstack/tethering/apishim/common/BpfCoordinatorShim.java
@@ -140,6 +140,8 @@
/**
* Adds a tethering IPv4 offload rule to appropriate BPF map.
+ *
+ * @return true iff the map was modified, false if the key already exists or there was an error.
*/
public abstract boolean tetherOffloadRuleAdd(boolean downstream, @NonNull Tether4Key key,
@NonNull Tether4Value value);
diff --git a/Tethering/src/com/android/networkstack/tethering/Tethering.java b/Tethering/src/com/android/networkstack/tethering/Tethering.java
index d62f18f..13f4f2a 100644
--- a/Tethering/src/com/android/networkstack/tethering/Tethering.java
+++ b/Tethering/src/com/android/networkstack/tethering/Tethering.java
@@ -2082,6 +2082,7 @@
chooseUpstreamType(true);
mTryCell = false;
}
+ mTetheringMetrics.initUpstreamUsageBaseline();
}
@Override
diff --git a/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java b/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
index 2202106..fc50faf 100644
--- a/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
+++ b/Tethering/src/com/android/networkstack/tethering/metrics/TetheringMetrics.java
@@ -16,6 +16,8 @@
package com.android.networkstack.tethering.metrics;
+import static android.app.usage.NetworkStats.Bucket.STATE_ALL;
+import static android.app.usage.NetworkStats.Bucket.TAG_NONE;
import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
@@ -24,6 +26,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
import static android.net.NetworkStats.METERED_YES;
+import static android.net.NetworkStats.UID_TETHERING;
import static android.net.NetworkTemplate.MATCH_BLUETOOTH;
import static android.net.NetworkTemplate.MATCH_ETHERNET;
import static android.net.NetworkTemplate.MATCH_MOBILE;
@@ -52,13 +55,19 @@
import static android.net.TetheringManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
import android.annotation.Nullable;
+import android.app.usage.NetworkStats;
+import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.net.NetworkCapabilities;
import android.net.NetworkTemplate;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
import android.stats.connectivity.DownstreamType;
import android.stats.connectivity.ErrorCode;
import android.stats.connectivity.UpstreamType;
import android.stats.connectivity.UserType;
+import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
@@ -75,6 +84,10 @@
/**
* Collection of utilities for tethering metrics.
*
+ * <p>This class is thread-safe. All accesses to this class will be either posting to the internal
+ * handler thread for processing or checking whether the access is from the internal handler
+ * thread. However, the constructor is an exception, as it is called on another thread.
+ *
* To see if the logs are properly sent to statsd, execute following commands
*
* $ adb shell cmd stats print-logs
@@ -93,11 +106,16 @@
*/
private static final String TETHER_UPSTREAM_DATA_USAGE_METRICS =
"tether_upstream_data_usage_metrics";
+ @VisibleForTesting
+ static final DataUsage EMPTY = new DataUsage(0L /* txBytes */, 0L /* rxBytes */);
private final SparseArray<NetworkTetheringReported.Builder> mBuilderMap = new SparseArray<>();
private final SparseArray<Long> mDownstreamStartTime = new SparseArray<Long>();
private final ArrayList<RecordUpstreamEvent> mUpstreamEventList = new ArrayList<>();
+ private final ArrayMap<UpstreamType, DataUsage> mUpstreamUsageBaseline = new ArrayMap<>();
private final Context mContext;
private final Dependencies mDependencies;
+ private final NetworkStatsManager mNetworkStatsManager;
+ private final Handler mHandler;
private UpstreamType mCurrentUpstream = null;
private Long mCurrentUpStreamStartTime = 0L;
@@ -136,6 +154,14 @@
return SdkLevel.isAtLeastT() && DeviceConfigUtils.isTetheringFeatureNotChickenedOut(
context, TETHER_UPSTREAM_DATA_USAGE_METRICS);
}
+
+ /**
+ * @see Handler
+ */
+ @NonNull
+ public Handler createHandler(Looper looper) {
+ return new Handler(looper);
+ }
}
/**
@@ -150,24 +176,49 @@
TetheringMetrics(Context context, Dependencies dependencies) {
mContext = context;
mDependencies = dependencies;
+ mNetworkStatsManager = mContext.getSystemService(NetworkStatsManager.class);
+ final HandlerThread thread = new HandlerThread(TAG);
+ thread.start();
+ mHandler = dependencies.createHandler(thread.getLooper());
}
- private static class DataUsage {
- final long mTxBytes;
- final long mRxBytes;
+ @VisibleForTesting
+ static class DataUsage {
+ public final long txBytes;
+ public final long rxBytes;
DataUsage(long txBytes, long rxBytes) {
- mTxBytes = txBytes;
- mRxBytes = rxBytes;
+ this.txBytes = txBytes;
+ this.rxBytes = rxBytes;
}
- public long getTxBytes() {
- return mTxBytes;
+ /*** Calculate the data usage delta from give new and old usage */
+ public static DataUsage subtract(DataUsage newUsage, DataUsage oldUsage) {
+ return new DataUsage(
+ newUsage.txBytes - oldUsage.txBytes,
+ newUsage.rxBytes - oldUsage.rxBytes);
}
- public long getRxBytes() {
- return mRxBytes;
+ @Override
+ public int hashCode() {
+ return (int) (txBytes & 0xFFFFFFFF)
+ + ((int) (txBytes >> 32) * 3)
+ + ((int) (rxBytes & 0xFFFFFFFF) * 5)
+ + ((int) (rxBytes >> 32) * 7);
}
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof DataUsage)) {
+ return false;
+ }
+ return txBytes == ((DataUsage) other).txBytes
+ && rxBytes == ((DataUsage) other).rxBytes;
+ }
+
}
private static class RecordUpstreamEvent {
@@ -194,6 +245,10 @@
* @param callerPkg The package name of the caller.
*/
public void createBuilder(final int downstreamType, final String callerPkg) {
+ mHandler.post(() -> handleCreateBuilder(downstreamType, callerPkg));
+ }
+
+ private void handleCreateBuilder(final int downstreamType, final String callerPkg) {
NetworkTetheringReported.Builder statsBuilder = NetworkTetheringReported.newBuilder()
.setDownstreamType(downstreamTypeToEnum(downstreamType))
.setUserType(userTypeToEnum(callerPkg))
@@ -211,6 +266,10 @@
* @param errCode The error code to set.
*/
public void updateErrorCode(final int downstreamType, final int errCode) {
+ mHandler.post(() -> handleUpdateErrorCode(downstreamType, errCode));
+ }
+
+ private void handleUpdateErrorCode(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!");
@@ -219,13 +278,26 @@
statsBuilder.setErrorCode(errorCodeToEnum(errCode));
}
- private DataUsage calculateDataUsage(@Nullable UpstreamType upstream) {
+ /**
+ * Calculates the data usage difference between the current and previous usage for the
+ * specified upstream type.
+ *
+ * @return A DataUsage object containing the calculated difference in transmitted (tx) and
+ * received (rx) bytes.
+ */
+ private DataUsage calculateDataUsageDelta(@Nullable UpstreamType upstream) {
if (upstream != null && mDependencies.isUpstreamDataUsageMetricsEnabled(mContext)
&& isUsageSupportedForUpstreamType(upstream)) {
- // TODO: Implement data usage calculation for the upstream type.
- return new DataUsage(0L, 0L);
+ final DataUsage oldUsage = mUpstreamUsageBaseline.getOrDefault(upstream, EMPTY);
+ if (oldUsage.equals(EMPTY)) {
+ Log.d(TAG, "No usage baseline for the upstream=" + upstream);
+ return EMPTY;
+ }
+ // TODO(b/352537247): Fix data usage which might be incorrect if the device uses
+ // tethering with the same upstream for over 15 days.
+ return DataUsage.subtract(getCurrentDataUsageForUpstreamType(upstream), oldUsage);
}
- return new DataUsage(0L, 0L);
+ return EMPTY;
}
/**
@@ -234,12 +306,16 @@
* @param ns The UpstreamNetworkState object representing the current upstream network state.
*/
public void maybeUpdateUpstreamType(@Nullable final UpstreamNetworkState ns) {
+ mHandler.post(() -> handleMaybeUpdateUpstreamType(ns));
+ }
+
+ private void handleMaybeUpdateUpstreamType(@Nullable final UpstreamNetworkState ns) {
UpstreamType upstream = transportTypeToUpstreamTypeEnum(ns);
if (upstream.equals(mCurrentUpstream)) return;
final long newTime = mDependencies.timeNow();
if (mCurrentUpstream != null) {
- final DataUsage dataUsage = calculateDataUsage(upstream);
+ final DataUsage dataUsage = calculateDataUsageDelta(mCurrentUpstream);
mUpstreamEventList.add(new RecordUpstreamEvent(mCurrentUpStreamStartTime, newTime,
mCurrentUpstream, dataUsage));
}
@@ -292,14 +368,14 @@
final long startTime = Math.max(downstreamStartTime, event.mStartTime);
// Handle completed upstream events.
addUpstreamEvent(upstreamEventsBuilder, startTime, event.mStopTime,
- event.mUpstreamType, event.mDataUsage.mTxBytes, event.mDataUsage.mRxBytes);
+ event.mUpstreamType, event.mDataUsage.txBytes, event.mDataUsage.rxBytes);
}
final long startTime = Math.max(downstreamStartTime, mCurrentUpStreamStartTime);
final long stopTime = mDependencies.timeNow();
// Handle the last upstream event.
- final DataUsage dataUsage = calculateDataUsage(mCurrentUpstream);
+ final DataUsage dataUsage = calculateDataUsageDelta(mCurrentUpstream);
addUpstreamEvent(upstreamEventsBuilder, startTime, stopTime, mCurrentUpstream,
- dataUsage.mTxBytes, dataUsage.mRxBytes);
+ dataUsage.txBytes, dataUsage.rxBytes);
statsBuilder.setUpstreamEvents(upstreamEventsBuilder);
statsBuilder.setDurationMillis(stopTime - downstreamStartTime);
}
@@ -315,6 +391,10 @@
* @param downstreamType the type of downstream event to remove statistics for
*/
public void sendReport(final int downstreamType) {
+ mHandler.post(() -> handleSendReport(downstreamType));
+ }
+
+ private void handleSendReport(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!");
@@ -335,8 +415,7 @@
*
* @param reported a NetworkTetheringReported object containing statistics to write
*/
- @VisibleForTesting
- public void write(@NonNull final NetworkTetheringReported reported) {
+ private void write(@NonNull final NetworkTetheringReported reported) {
final byte[] upstreamEvents = reported.getUpstreamEvents().toByteArray();
mDependencies.write(reported);
if (DBG) {
@@ -358,12 +437,67 @@
}
/**
+ * Initialize the upstream data usage baseline when tethering is turned on.
+ */
+ public void initUpstreamUsageBaseline() {
+ mHandler.post(() -> handleInitUpstreamUsageBaseline());
+ }
+
+ private void handleInitUpstreamUsageBaseline() {
+ if (!(mDependencies.isUpstreamDataUsageMetricsEnabled(mContext)
+ && mUpstreamUsageBaseline.isEmpty())) {
+ return;
+ }
+
+ for (UpstreamType type : UpstreamType.values()) {
+ if (!isUsageSupportedForUpstreamType(type)) continue;
+ mUpstreamUsageBaseline.put(type, getCurrentDataUsageForUpstreamType(type));
+ }
+ }
+
+ @VisibleForTesting
+ @NonNull
+ DataUsage getDataUsageFromUpstreamType(@NonNull UpstreamType type) {
+ if (mHandler.getLooper().getThread() != Thread.currentThread()) {
+ throw new IllegalStateException(
+ "Not running on Handler thread: " + Thread.currentThread().getName());
+ }
+ return mUpstreamUsageBaseline.getOrDefault(type, EMPTY);
+ }
+
+
+ /**
+ * Get the current usage for given upstream type.
+ */
+ @NonNull
+ private DataUsage getCurrentDataUsageForUpstreamType(@NonNull UpstreamType type) {
+ final NetworkStats stats = mNetworkStatsManager.queryDetailsForUidTagState(
+ buildNetworkTemplateForUpstreamType(type), Long.MIN_VALUE, Long.MAX_VALUE,
+ UID_TETHERING, TAG_NONE, STATE_ALL);
+
+ final NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+ Long totalTxBytes = 0L;
+ Long totalRxBytes = 0L;
+ while (stats.hasNextBucket()) {
+ stats.getNextBucket(bucket);
+ totalTxBytes += bucket.getTxBytes();
+ totalRxBytes += bucket.getRxBytes();
+ }
+ return new DataUsage(totalTxBytes, totalRxBytes);
+ }
+
+ /**
* Cleans up the variables related to upstream events when tethering is turned off.
*/
public void cleanup() {
+ mHandler.post(() -> handleCleanup());
+ }
+
+ private void handleCleanup() {
mUpstreamEventList.clear();
mCurrentUpstream = null;
mCurrentUpStreamStartTime = 0L;
+ mUpstreamUsageBaseline.clear();
}
private DownstreamType downstreamTypeToEnum(final int ifaceType) {
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
index fbc2893..34689bc 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/metrics/TetheringMetricsTest.java
@@ -16,12 +16,19 @@
package com.android.networkstack.tethering.metrics;
+import static android.app.usage.NetworkStats.Bucket.STATE_ALL;
+import static android.app.usage.NetworkStats.Bucket.TAG_NONE;
import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
import static android.net.NetworkCapabilities.TRANSPORT_LOWPAN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
+import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.UID_TETHERING;
import static android.net.NetworkTemplate.MATCH_BLUETOOTH;
import static android.net.NetworkTemplate.MATCH_ETHERNET;
import static android.net.NetworkTemplate.MATCH_MOBILE;
@@ -49,29 +56,47 @@
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 android.stats.connectivity.UpstreamType.UT_BLUETOOTH;
+import static android.stats.connectivity.UpstreamType.UT_CELLULAR;
+import static android.stats.connectivity.UpstreamType.UT_ETHERNET;
+import static android.stats.connectivity.UpstreamType.UT_WIFI;
+
+import static com.android.networkstack.tethering.metrics.TetheringMetrics.EMPTY;
+import static com.android.testutils.NetworkStatsUtilsKt.makePublicStatsFromAndroidNetStats;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
+import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.net.NetworkCapabilities;
+import android.net.NetworkStats;
import android.net.NetworkTemplate;
import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.stats.connectivity.DownstreamType;
import android.stats.connectivity.ErrorCode;
import android.stats.connectivity.UpstreamType;
import android.stats.connectivity.UserType;
+import android.util.ArrayMap;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.networkstack.tethering.UpstreamNetworkState;
+import com.android.networkstack.tethering.metrics.TetheringMetrics.DataUsage;
import com.android.networkstack.tethering.metrics.TetheringMetrics.Dependencies;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
+import com.android.testutils.HandlerUtils;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -90,14 +115,19 @@
private static final String GMS_PKG = "com.google.android.gms";
private static final long TEST_START_TIME = 1670395936033L;
private static final long SECOND_IN_MILLIS = 1_000L;
+ private static final long DEFAULT_TIMEOUT = 2000L;
private static final int MATCH_NONE = -1;
@Mock private Context mContext;
@Mock private Dependencies mDeps;
+ @Mock private NetworkStatsManager mNetworkStatsManager;
private TetheringMetrics mTetheringMetrics;
private final NetworkTetheringReported.Builder mStatsBuilder =
NetworkTetheringReported.newBuilder();
+ private final ArrayMap<UpstreamType, DataUsage> mMockUpstreamUsageBaseline = new ArrayMap<>();
+ private HandlerThread mThread;
+ private Handler mHandler;
private long mElapsedRealtime;
@@ -124,10 +154,35 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
doReturn(TEST_START_TIME).when(mDeps).timeNow();
+ doReturn(mNetworkStatsManager).when(mContext).getSystemService(NetworkStatsManager.class);
+ mThread = new HandlerThread("TetheringMetricsTest");
+ mThread.start();
+ mHandler = new Handler(mThread.getLooper());
+ doReturn(mHandler).when(mDeps).createHandler(any());
+ // Set up the usage for upstream types.
+ mMockUpstreamUsageBaseline.put(UT_CELLULAR, new DataUsage(100L, 200L));
+ mMockUpstreamUsageBaseline.put(UT_WIFI, new DataUsage(400L, 800L));
+ mMockUpstreamUsageBaseline.put(UT_BLUETOOTH, new DataUsage(50L, 80L));
+ mMockUpstreamUsageBaseline.put(UT_ETHERNET, new DataUsage(0L, 0L));
+ doAnswer(inv -> {
+ final NetworkTemplate template = (NetworkTemplate) inv.getArguments()[0];
+ final DataUsage dataUsage = mMockUpstreamUsageBaseline.getOrDefault(
+ matchRuleToUpstreamType(template.getMatchRule()), new DataUsage(0L, 0L));
+ return makeNetworkStatsWithTxRxBytes(dataUsage);
+ }).when(mNetworkStatsManager).queryDetailsForUidTagState(any(), eq(Long.MIN_VALUE),
+ eq(Long.MAX_VALUE), eq(UID_TETHERING), eq(TAG_NONE), eq(STATE_ALL));
mTetheringMetrics = new TetheringMetrics(mContext, mDeps);
mElapsedRealtime = 0L;
}
+ @After
+ public void tearDown() throws Exception {
+ if (mThread != null) {
+ mThread.quitSafely();
+ mThread.join();
+ }
+ }
+
private void verifyReport(final DownstreamType downstream, final ErrorCode error,
final UserType user, final UpstreamEvents.Builder upstreamEvents, final long duration)
throws Exception {
@@ -142,9 +197,15 @@
verify(mDeps).write(expectedReport);
}
+ private void runAndWaitForIdle(Runnable r) {
+ r.run();
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ }
+
private void updateErrorAndSendReport(final int downstream, final int error) {
mTetheringMetrics.updateErrorCode(downstream, error);
mTetheringMetrics.sendReport(downstream);
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
}
private static NetworkCapabilities buildUpstreamCapabilities(final int[] transports) {
@@ -176,7 +237,7 @@
private void runDownstreamTypesTest(final int type, final DownstreamType expectedResult)
throws Exception {
mTetheringMetrics = new TetheringMetrics(mContext, mDeps);
- mTetheringMetrics.createBuilder(type, TEST_CALLER_PKG);
+ runAndWaitForIdle(() -> mTetheringMetrics.createBuilder(type, TEST_CALLER_PKG));
final long duration = 2 * SECOND_IN_MILLIS;
incrementCurrentTime(duration);
UpstreamEvents.Builder upstreamEvents = UpstreamEvents.newBuilder();
@@ -202,14 +263,15 @@
private void runErrorCodesTest(final int errorCode, final ErrorCode expectedResult)
throws Exception {
mTetheringMetrics = new TetheringMetrics(mContext, mDeps);
- mTetheringMetrics.createBuilder(TETHERING_WIFI, TEST_CALLER_PKG);
- mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_WIFI));
+ runAndWaitForIdle(() -> mTetheringMetrics.createBuilder(TETHERING_WIFI, TEST_CALLER_PKG));
+ runAndWaitForIdle(() ->
+ mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_WIFI)));
final long duration = 2 * SECOND_IN_MILLIS;
incrementCurrentTime(duration);
updateErrorAndSendReport(TETHERING_WIFI, errorCode);
UpstreamEvents.Builder upstreamEvents = UpstreamEvents.newBuilder();
- addUpstreamEvent(upstreamEvents, UpstreamType.UT_WIFI, duration, 0L, 0L);
+ addUpstreamEvent(upstreamEvents, UT_WIFI, duration, 0L, 0L);
verifyReport(DownstreamType.DS_TETHERING_WIFI, expectedResult, UserType.USER_UNKNOWN,
upstreamEvents, getElapsedRealtime());
clearElapsedRealtime();
@@ -243,7 +305,7 @@
private void runUserTypesTest(final String callerPkg, final UserType expectedResult)
throws Exception {
mTetheringMetrics = new TetheringMetrics(mContext, mDeps);
- mTetheringMetrics.createBuilder(TETHERING_WIFI, callerPkg);
+ runAndWaitForIdle(() -> mTetheringMetrics.createBuilder(TETHERING_WIFI, callerPkg));
final long duration = 1 * SECOND_IN_MILLIS;
incrementCurrentTime(duration);
updateErrorAndSendReport(TETHERING_WIFI, TETHER_ERROR_NO_ERROR);
@@ -267,8 +329,8 @@
private void runUpstreamTypesTest(final UpstreamNetworkState ns,
final UpstreamType expectedResult) throws Exception {
mTetheringMetrics = new TetheringMetrics(mContext, mDeps);
- mTetheringMetrics.createBuilder(TETHERING_WIFI, TEST_CALLER_PKG);
- mTetheringMetrics.maybeUpdateUpstreamType(ns);
+ runAndWaitForIdle(() -> mTetheringMetrics.createBuilder(TETHERING_WIFI, TEST_CALLER_PKG));
+ runAndWaitForIdle(() -> mTetheringMetrics.maybeUpdateUpstreamType(ns));
final long duration = 2 * SECOND_IN_MILLIS;
incrementCurrentTime(duration);
updateErrorAndSendReport(TETHERING_WIFI, TETHER_ERROR_NO_ERROR);
@@ -283,10 +345,10 @@
@Test
public void testUpstreamTypes() throws Exception {
runUpstreamTypesTest(null , UpstreamType.UT_NO_NETWORK);
- runUpstreamTypesTest(buildUpstreamState(TRANSPORT_CELLULAR), UpstreamType.UT_CELLULAR);
- runUpstreamTypesTest(buildUpstreamState(TRANSPORT_WIFI), UpstreamType.UT_WIFI);
- runUpstreamTypesTest(buildUpstreamState(TRANSPORT_BLUETOOTH), UpstreamType.UT_BLUETOOTH);
- runUpstreamTypesTest(buildUpstreamState(TRANSPORT_ETHERNET), UpstreamType.UT_ETHERNET);
+ runUpstreamTypesTest(buildUpstreamState(TRANSPORT_CELLULAR), UT_CELLULAR);
+ runUpstreamTypesTest(buildUpstreamState(TRANSPORT_WIFI), UT_WIFI);
+ runUpstreamTypesTest(buildUpstreamState(TRANSPORT_BLUETOOTH), UT_BLUETOOTH);
+ runUpstreamTypesTest(buildUpstreamState(TRANSPORT_ETHERNET), UT_ETHERNET);
runUpstreamTypesTest(buildUpstreamState(TRANSPORT_WIFI_AWARE), UpstreamType.UT_WIFI_AWARE);
runUpstreamTypesTest(buildUpstreamState(TRANSPORT_LOWPAN), UpstreamType.UT_LOWPAN);
runUpstreamTypesTest(buildUpstreamState(TRANSPORT_CELLULAR, TRANSPORT_WIFI,
@@ -295,13 +357,13 @@
@Test
public void testMultiBuildersCreatedBeforeSendReport() throws Exception {
- mTetheringMetrics.createBuilder(TETHERING_WIFI, SETTINGS_PKG);
+ runAndWaitForIdle(() -> mTetheringMetrics.createBuilder(TETHERING_WIFI, SETTINGS_PKG));
final long wifiTetheringStartTime = currentTimeMillis();
incrementCurrentTime(1 * SECOND_IN_MILLIS);
- mTetheringMetrics.createBuilder(TETHERING_USB, SYSTEMUI_PKG);
+ runAndWaitForIdle(() -> mTetheringMetrics.createBuilder(TETHERING_USB, SYSTEMUI_PKG));
final long usbTetheringStartTime = currentTimeMillis();
incrementCurrentTime(2 * SECOND_IN_MILLIS);
- mTetheringMetrics.createBuilder(TETHERING_BLUETOOTH, GMS_PKG);
+ runAndWaitForIdle(() -> mTetheringMetrics.createBuilder(TETHERING_BLUETOOTH, GMS_PKG));
final long bluetoothTetheringStartTime = currentTimeMillis();
incrementCurrentTime(3 * SECOND_IN_MILLIS);
updateErrorAndSendReport(TETHERING_WIFI, TETHER_ERROR_DHCPSERVER_ERROR);
@@ -335,19 +397,20 @@
@Test
public void testUpstreamsWithMultipleDownstreams() throws Exception {
- mTetheringMetrics.createBuilder(TETHERING_WIFI, SETTINGS_PKG);
+ runAndWaitForIdle(() -> mTetheringMetrics.createBuilder(TETHERING_WIFI, SETTINGS_PKG));
final long wifiTetheringStartTime = currentTimeMillis();
incrementCurrentTime(1 * SECOND_IN_MILLIS);
- mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_WIFI));
+ runAndWaitForIdle(() ->
+ mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_WIFI)));
final long wifiUpstreamStartTime = currentTimeMillis();
incrementCurrentTime(5 * SECOND_IN_MILLIS);
- mTetheringMetrics.createBuilder(TETHERING_USB, SYSTEMUI_PKG);
+ runAndWaitForIdle(() -> mTetheringMetrics.createBuilder(TETHERING_USB, SYSTEMUI_PKG));
final long usbTetheringStartTime = currentTimeMillis();
incrementCurrentTime(5 * SECOND_IN_MILLIS);
updateErrorAndSendReport(TETHERING_USB, TETHER_ERROR_NO_ERROR);
UpstreamEvents.Builder usbTetheringUpstreamEvents = UpstreamEvents.newBuilder();
- addUpstreamEvent(usbTetheringUpstreamEvents, UpstreamType.UT_WIFI,
+ addUpstreamEvent(usbTetheringUpstreamEvents, UT_WIFI,
currentTimeMillis() - usbTetheringStartTime, 0L, 0L);
verifyReport(DownstreamType.DS_TETHERING_USB, ErrorCode.EC_NO_ERROR,
UserType.USER_SYSTEMUI, usbTetheringUpstreamEvents,
@@ -356,7 +419,7 @@
updateErrorAndSendReport(TETHERING_WIFI, TETHER_ERROR_NO_ERROR);
UpstreamEvents.Builder wifiTetheringUpstreamEvents = UpstreamEvents.newBuilder();
- addUpstreamEvent(wifiTetheringUpstreamEvents, UpstreamType.UT_WIFI,
+ addUpstreamEvent(wifiTetheringUpstreamEvents, UT_WIFI,
currentTimeMillis() - wifiUpstreamStartTime, 0L, 0L);
verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_NO_ERROR,
UserType.USER_SETTINGS, wifiTetheringUpstreamEvents,
@@ -365,24 +428,27 @@
@Test
public void testSwitchingMultiUpstreams() throws Exception {
- mTetheringMetrics.createBuilder(TETHERING_WIFI, SETTINGS_PKG);
+ runAndWaitForIdle(() -> mTetheringMetrics.createBuilder(TETHERING_WIFI, SETTINGS_PKG));
final long wifiTetheringStartTime = currentTimeMillis();
incrementCurrentTime(1 * SECOND_IN_MILLIS);
- mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_WIFI));
+ runAndWaitForIdle(() ->
+ mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_WIFI)));
final long wifiDuration = 5 * SECOND_IN_MILLIS;
incrementCurrentTime(wifiDuration);
- mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_BLUETOOTH));
+ runAndWaitForIdle(() ->
+ mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_BLUETOOTH)));
final long bluetoothDuration = 15 * SECOND_IN_MILLIS;
incrementCurrentTime(bluetoothDuration);
- mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_CELLULAR));
+ runAndWaitForIdle(() ->
+ mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_CELLULAR)));
final long celltoothDuration = 20 * SECOND_IN_MILLIS;
incrementCurrentTime(celltoothDuration);
updateErrorAndSendReport(TETHERING_WIFI, TETHER_ERROR_NO_ERROR);
UpstreamEvents.Builder upstreamEvents = UpstreamEvents.newBuilder();
- addUpstreamEvent(upstreamEvents, UpstreamType.UT_WIFI, wifiDuration, 0L, 0L);
- addUpstreamEvent(upstreamEvents, UpstreamType.UT_BLUETOOTH, bluetoothDuration, 0L, 0L);
- addUpstreamEvent(upstreamEvents, UpstreamType.UT_CELLULAR, celltoothDuration, 0L, 0L);
+ addUpstreamEvent(upstreamEvents, UT_WIFI, wifiDuration, 0L, 0L);
+ addUpstreamEvent(upstreamEvents, UT_BLUETOOTH, bluetoothDuration, 0L, 0L);
+ addUpstreamEvent(upstreamEvents, UT_CELLULAR, celltoothDuration, 0L, 0L);
verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_NO_ERROR,
UserType.USER_SETTINGS, upstreamEvents,
@@ -397,10 +463,10 @@
@Test
public void testUsageSupportedForUpstreamTypeTest() {
- runUsageSupportedForUpstreamTypeTest(UpstreamType.UT_CELLULAR, true /* isSupported */);
- runUsageSupportedForUpstreamTypeTest(UpstreamType.UT_WIFI, true /* isSupported */);
- runUsageSupportedForUpstreamTypeTest(UpstreamType.UT_BLUETOOTH, true /* isSupported */);
- runUsageSupportedForUpstreamTypeTest(UpstreamType.UT_ETHERNET, true /* isSupported */);
+ runUsageSupportedForUpstreamTypeTest(UT_CELLULAR, true /* isSupported */);
+ runUsageSupportedForUpstreamTypeTest(UT_WIFI, true /* isSupported */);
+ runUsageSupportedForUpstreamTypeTest(UT_BLUETOOTH, true /* isSupported */);
+ runUsageSupportedForUpstreamTypeTest(UT_ETHERNET, true /* isSupported */);
runUsageSupportedForUpstreamTypeTest(UpstreamType.UT_WIFI_AWARE, false /* isSupported */);
runUsageSupportedForUpstreamTypeTest(UpstreamType.UT_LOWPAN, false /* isSupported */);
runUsageSupportedForUpstreamTypeTest(UpstreamType.UT_UNKNOWN, false /* isSupported */);
@@ -420,12 +486,138 @@
@Test
@IgnoreUpTo(Build.VERSION_CODES.S_V2)
public void testBuildNetworkTemplateForUpstreamType() {
- runBuildNetworkTemplateForUpstreamType(UpstreamType.UT_CELLULAR, MATCH_MOBILE);
- runBuildNetworkTemplateForUpstreamType(UpstreamType.UT_WIFI, MATCH_WIFI);
- runBuildNetworkTemplateForUpstreamType(UpstreamType.UT_BLUETOOTH, MATCH_BLUETOOTH);
- runBuildNetworkTemplateForUpstreamType(UpstreamType.UT_ETHERNET, MATCH_ETHERNET);
+ runBuildNetworkTemplateForUpstreamType(UT_CELLULAR, MATCH_MOBILE);
+ runBuildNetworkTemplateForUpstreamType(UT_WIFI, MATCH_WIFI);
+ runBuildNetworkTemplateForUpstreamType(UT_BLUETOOTH, MATCH_BLUETOOTH);
+ runBuildNetworkTemplateForUpstreamType(UT_ETHERNET, MATCH_ETHERNET);
runBuildNetworkTemplateForUpstreamType(UpstreamType.UT_WIFI_AWARE, MATCH_NONE);
runBuildNetworkTemplateForUpstreamType(UpstreamType.UT_LOWPAN, MATCH_NONE);
runBuildNetworkTemplateForUpstreamType(UpstreamType.UT_UNKNOWN, MATCH_NONE);
}
+
+ private void verifyEmptyUsageForAllUpstreamTypes() {
+ mHandler.post(() -> {
+ for (UpstreamType type : UpstreamType.values()) {
+ assertEquals(EMPTY, mTetheringMetrics.getDataUsageFromUpstreamType(type));
+ }
+ });
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+ }
+
+ @Test
+ public void testInitializeUpstreamDataUsageBeforeT() {
+ // Verify the usage is empty for all upstream types before initialization.
+ verifyEmptyUsageForAllUpstreamTypes();
+
+ // Verify the usage is still empty after initialization if sdk is lower than T.
+ doReturn(false).when(mDeps).isUpstreamDataUsageMetricsEnabled(any());
+ runAndWaitForIdle(() -> mTetheringMetrics.initUpstreamUsageBaseline());
+ verifyEmptyUsageForAllUpstreamTypes();
+ }
+
+ private android.app.usage.NetworkStats makeNetworkStatsWithTxRxBytes(DataUsage dataUsage) {
+ final NetworkStats testAndroidNetStats =
+ new NetworkStats(0L /* elapsedRealtime */, 1 /* initialSize */).addEntry(
+ new NetworkStats.Entry("test", 10001, SET_DEFAULT, TAG_NONE,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_YES, dataUsage.rxBytes,
+ 10, dataUsage.txBytes, 10, 10));
+ return makePublicStatsFromAndroidNetStats(testAndroidNetStats);
+ }
+
+ private static UpstreamType matchRuleToUpstreamType(int matchRule) {
+ switch (matchRule) {
+ case MATCH_MOBILE:
+ return UT_CELLULAR;
+ case MATCH_WIFI:
+ return UT_WIFI;
+ case MATCH_BLUETOOTH:
+ return UT_BLUETOOTH;
+ case MATCH_ETHERNET:
+ return UT_ETHERNET;
+ default:
+ return UpstreamType.UT_UNKNOWN;
+ }
+ }
+
+ private void initializeUpstreamUsageBaseline() {
+ doReturn(true).when(mDeps).isUpstreamDataUsageMetricsEnabled(any());
+ runAndWaitForIdle(() -> mTetheringMetrics.initUpstreamUsageBaseline());
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testInitUpstreamUsageBaselineAndCleanup() {
+ // Verify the usage is empty for all upstream types before initialization.
+ verifyEmptyUsageForAllUpstreamTypes();
+
+ // Verify the usage has been initialized
+ initializeUpstreamUsageBaseline();
+
+ mHandler.post(() -> {
+ for (UpstreamType type : UpstreamType.values()) {
+ final DataUsage dataUsage = mTetheringMetrics.getDataUsageFromUpstreamType(type);
+ if (TetheringMetrics.isUsageSupportedForUpstreamType(type)) {
+ assertEquals(mMockUpstreamUsageBaseline.get(type), dataUsage);
+ } else {
+ assertEquals(EMPTY, dataUsage);
+ }
+ }
+ });
+ HandlerUtils.waitForIdle(mHandler, DEFAULT_TIMEOUT);
+
+ // Verify the usage is empty after clean up
+ runAndWaitForIdle(() -> mTetheringMetrics.cleanup());
+ verifyEmptyUsageForAllUpstreamTypes();
+ }
+
+ private void updateUpstreamDataUsage(UpstreamType type, long usageDiff) {
+ final DataUsage oldWifiUsage = mMockUpstreamUsageBaseline.get(type);
+ final DataUsage newWifiUsage = new DataUsage(
+ oldWifiUsage.txBytes + usageDiff,
+ oldWifiUsage.rxBytes + usageDiff);
+ mMockUpstreamUsageBaseline.put(type, newWifiUsage);
+ }
+
+ @Test
+ @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ public void testDataUsageCalculation() throws Exception {
+ initializeUpstreamUsageBaseline();
+ runAndWaitForIdle(() -> mTetheringMetrics.createBuilder(TETHERING_WIFI, SETTINGS_PKG));
+ final long wifiTetheringStartTime = currentTimeMillis();
+ incrementCurrentTime(1 * SECOND_IN_MILLIS);
+
+ // Change the upstream to Wi-Fi and update the data usage
+ runAndWaitForIdle(() ->
+ mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_WIFI)));
+ final long wifiDuration = 5 * SECOND_IN_MILLIS;
+ final long wifiUsageDiff = 100L;
+ incrementCurrentTime(wifiDuration);
+ updateUpstreamDataUsage(UT_WIFI, wifiUsageDiff);
+
+ // Change the upstream to bluetooth and update the data usage
+ runAndWaitForIdle(() ->
+ mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_BLUETOOTH)));
+ final long bluetoothDuration = 15 * SECOND_IN_MILLIS;
+ final long btUsageDiff = 50L;
+ incrementCurrentTime(bluetoothDuration);
+ updateUpstreamDataUsage(UT_BLUETOOTH, btUsageDiff);
+
+ // Change the upstream to cellular and update the data usage
+ runAndWaitForIdle(() ->
+ mTetheringMetrics.maybeUpdateUpstreamType(buildUpstreamState(TRANSPORT_CELLULAR)));
+ final long cellDuration = 20 * SECOND_IN_MILLIS;
+ final long cellUsageDiff = 500L;
+ incrementCurrentTime(cellDuration);
+ updateUpstreamDataUsage(UT_CELLULAR, cellUsageDiff);
+
+ // Stop tethering and verify that the data usage is uploaded.
+ updateErrorAndSendReport(TETHERING_WIFI, TETHER_ERROR_NO_ERROR);
+ UpstreamEvents.Builder upstreamEvents = UpstreamEvents.newBuilder();
+ addUpstreamEvent(upstreamEvents, UT_WIFI, wifiDuration, wifiUsageDiff, wifiUsageDiff);
+ addUpstreamEvent(upstreamEvents, UT_BLUETOOTH, bluetoothDuration, btUsageDiff, btUsageDiff);
+ addUpstreamEvent(upstreamEvents, UT_CELLULAR, cellDuration, cellUsageDiff, cellUsageDiff);
+ verifyReport(DownstreamType.DS_TETHERING_WIFI, ErrorCode.EC_NO_ERROR,
+ UserType.USER_SETTINGS, upstreamEvents,
+ currentTimeMillis() - wifiTetheringStartTime);
+ }
}
diff --git a/bpf/headers/include/bpf_helpers.h b/bpf/headers/include/bpf_helpers.h
index ca0ca76..ac5ffda 100644
--- a/bpf/headers/include/bpf_helpers.h
+++ b/bpf/headers/include/bpf_helpers.h
@@ -50,18 +50,21 @@
// Note: this value (and the following +1u's) are hardcoded in NetBpfLoad.cpp
#define BPFLOADER_MAINLINE_VERSION 42u
-// Android Mainline BpfLoader when running on Android T
+// Android Mainline BpfLoader when running on Android T (sdk=33)
#define BPFLOADER_MAINLINE_T_VERSION (BPFLOADER_MAINLINE_VERSION + 1u)
-// Android Mainline BpfLoader when running on Android U
+// Android Mainline BpfLoader when running on Android U (sdk=34)
#define BPFLOADER_MAINLINE_U_VERSION (BPFLOADER_MAINLINE_T_VERSION + 1u)
// Android Mainline BpfLoader when running on Android U QPR3
#define BPFLOADER_MAINLINE_U_QPR3_VERSION (BPFLOADER_MAINLINE_U_VERSION + 1u)
-// Android Mainline BpfLoader when running on Android V
+// Android Mainline BpfLoader when running on Android V (sdk=35)
#define BPFLOADER_MAINLINE_V_VERSION (BPFLOADER_MAINLINE_U_QPR3_VERSION + 1u)
+// Android Mainline BpfLoader when running on Android W (sdk=36)
+#define BPFLOADER_MAINLINE_W_VERSION (BPFLOADER_MAINLINE_V_VERSION + 1u)
+
/* For mainline module use, you can #define BPFLOADER_{MIN/MAX}_VER
* before #include "bpf_helpers.h" to change which bpfloaders will
* process the resulting .o file.
@@ -288,6 +291,12 @@
bpf_ringbuf_submit_unsafe(v, 0); \
}
+#define DEFINE_BPF_RINGBUF(the_map, ValueType, size_bytes, usr, grp, md) \
+ DEFINE_BPF_RINGBUF_EXT(the_map, ValueType, size_bytes, usr, grp, md, \
+ DEFAULT_BPF_MAP_SELINUX_CONTEXT, DEFAULT_BPF_MAP_PIN_SUBDIR, \
+ PRIVATE, BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, \
+ LOAD_ON_ENG, LOAD_ON_USER, LOAD_ON_USERDEBUG)
+
/* There exist buggy kernels with pre-T OS, that due to
* kernel patch "[ALPS05162612] bpf: fix ubsan error"
* do not support userspace writes into non-zero index of bpf map arrays.
@@ -346,11 +355,17 @@
#error "Bpf Map UID must be left at default of AID_ROOT for BpfLoader prior to v0.28"
#endif
-#define DEFINE_BPF_MAP_UGM(the_map, TYPE, KeyType, ValueType, num_entries, usr, grp, md) \
- DEFINE_BPF_MAP_EXT(the_map, TYPE, KeyType, ValueType, num_entries, usr, grp, md, \
- DEFAULT_BPF_MAP_SELINUX_CONTEXT, DEFAULT_BPF_MAP_PIN_SUBDIR, PRIVATE, \
- BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, LOAD_ON_ENG, \
- LOAD_ON_USER, LOAD_ON_USERDEBUG)
+// for maps not meant to be accessed from userspace
+#define DEFINE_BPF_MAP_KERNEL_INTERNAL(the_map, TYPE, KeyType, ValueType, num_entries) \
+ DEFINE_BPF_MAP_EXT(the_map, TYPE, KeyType, ValueType, num_entries, AID_ROOT, AID_ROOT, \
+ 0000, "fs_bpf_loader", "", PRIVATE, BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, \
+ LOAD_ON_ENG, LOAD_ON_USER, LOAD_ON_USERDEBUG)
+
+#define DEFINE_BPF_MAP_UGM(the_map, TYPE, KeyType, ValueType, num_entries, usr, grp, md) \
+ DEFINE_BPF_MAP_EXT(the_map, TYPE, KeyType, ValueType, num_entries, usr, grp, md, \
+ DEFAULT_BPF_MAP_SELINUX_CONTEXT, DEFAULT_BPF_MAP_PIN_SUBDIR, \
+ PRIVATE, BPFLOADER_MIN_VER, BPFLOADER_MAX_VER, \
+ LOAD_ON_ENG, LOAD_ON_USER, LOAD_ON_USERDEBUG)
#define DEFINE_BPF_MAP(the_map, TYPE, KeyType, ValueType, num_entries) \
DEFINE_BPF_MAP_UGM(the_map, TYPE, KeyType, ValueType, num_entries, \
@@ -389,6 +404,7 @@
static long (*bpf_get_stackid)(void* ctx, void* map, uint64_t flags) = (void*) BPF_FUNC_get_stackid;
static long (*bpf_get_current_comm)(void* buf, uint32_t buf_size) = (void*) BPF_FUNC_get_current_comm;
+// GPL only:
static int (*bpf_trace_printk)(const char* fmt, int fmt_size, ...) = (void*) BPF_FUNC_trace_printk;
#define bpf_printf(s, n...) bpf_trace_printk(s, sizeof(s), ## n)
// Note: bpf only supports up to 3 arguments, log via: bpf_printf("msg %d %d %d", 1, 2, 3);
diff --git a/bpf/loader/NetBpfLoad.cpp b/bpf/loader/NetBpfLoad.cpp
index c058433..4767dfa 100644
--- a/bpf/loader/NetBpfLoad.cpp
+++ b/bpf/loader/NetBpfLoad.cpp
@@ -60,7 +60,14 @@
#include "bpf_map_def.h"
using android::base::EndsWith;
+using android::base::GetIntProperty;
+using android::base::GetProperty;
+using android::base::InitLogging;
+using android::base::KernelLogger;
+using android::base::SetProperty;
+using android::base::Split;
using android::base::StartsWith;
+using android::base::Tokenize;
using android::base::unique_fd;
using std::ifstream;
using std::ios;
@@ -90,6 +97,8 @@
net_shared, // (T+) fs_bpf_net_shared /sys/fs/bpf/net_shared
netd_readonly, // (T+) fs_bpf_netd_readonly /sys/fs/bpf/netd_readonly
netd_shared, // (T+) fs_bpf_netd_shared /sys/fs/bpf/netd_shared
+ loader, // (U+) fs_bpf_loader /sys/fs/bpf/loader
+ // on T due to lack of sepolicy/genfscon rules it behaves simply as 'fs_bpf'
};
static constexpr domain AllDomains[] = {
@@ -99,6 +108,7 @@
domain::net_shared,
domain::netd_readonly,
domain::netd_shared,
+ domain::loader,
};
static constexpr bool specified(domain d) {
@@ -112,7 +122,7 @@
// Returns the build type string (from ro.build.type).
const std::string& getBuildType() {
- static std::string t = android::base::GetProperty("ro.build.type", "unknown");
+ static std::string t = GetProperty("ro.build.type", "unknown");
return t;
}
@@ -144,6 +154,7 @@
case domain::net_shared: return "fs_bpf_net_shared";
case domain::netd_readonly: return "fs_bpf_netd_readonly";
case domain::netd_shared: return "fs_bpf_netd_shared";
+ case domain::loader: return "fs_bpf_loader";
}
}
@@ -167,6 +178,7 @@
case domain::net_shared: return "net_shared/";
case domain::netd_readonly: return "netd_readonly/";
case domain::netd_shared: return "netd_shared/";
+ case domain::loader: return "loader/";
}
};
@@ -184,7 +196,7 @@
static string pathToObjName(const string& path) {
// extract everything after the final slash, ie. this is the filename 'foo@1.o' or 'bar.o'
- string filename = android::base::Split(path, "/").back();
+ string filename = Split(path, "/").back();
// strip off everything from the final period onwards (strip '.o' suffix), ie. 'foo@1' or 'bar'
string name = filename.substr(0, filename.find_last_of('.'));
// strip any potential @1 suffix, this will leave us with just 'foo' or 'bar'
@@ -1016,7 +1028,7 @@
if (!fd.ok()) {
if (log_buf.size()) {
- vector<string> lines = android::base::Split(log_buf.data(), "\n");
+ vector<string> lines = Split(log_buf.data(), "\n");
ALOGW("BPF_PROG_LOAD - BEGIN log_buf contents:");
for (const auto& line : lines) ALOGW("%s", line.c_str());
@@ -1247,7 +1259,7 @@
// to include a newline to match 'echo "value" > /proc/sys/...foo' behaviour,
// which is usually how kernel devs test the actual sysctl interfaces.
static int writeProcSysFile(const char *filename, const char *value) {
- base::unique_fd fd(open(filename, O_WRONLY | O_CLOEXEC));
+ unique_fd fd(open(filename, O_WRONLY | O_CLOEXEC));
if (fd < 0) {
const int err = errno;
ALOGE("open('%s', O_WRONLY | O_CLOEXEC) -> %s", filename, strerror(err));
@@ -1324,7 +1336,7 @@
}
static bool hasGSM() {
- static string ph = base::GetProperty("gsm.current.phone-type", "");
+ static string ph = GetProperty("gsm.current.phone-type", "");
static bool gsm = (ph != "");
static bool logged = false;
if (!logged) {
@@ -1337,7 +1349,7 @@
static bool isTV() {
if (hasGSM()) return false; // TVs don't do GSM
- static string key = base::GetProperty("ro.oem.key1", "");
+ static string key = GetProperty("ro.oem.key1", "");
static bool tv = StartsWith(key, "ATV00");
static bool logged = false;
if (!logged) {
@@ -1348,10 +1360,10 @@
}
static bool isWear() {
- static string wearSdkStr = base::GetProperty("ro.cw_build.wear_sdk.version", "");
- static int wearSdkInt = base::GetIntProperty("ro.cw_build.wear_sdk.version", 0);
- static string buildChars = base::GetProperty("ro.build.characteristics", "");
- static vector<string> v = base::Tokenize(buildChars, ",");
+ static string wearSdkStr = GetProperty("ro.cw_build.wear_sdk.version", "");
+ static int wearSdkInt = GetIntProperty("ro.cw_build.wear_sdk.version", 0);
+ static string buildChars = GetProperty("ro.build.characteristics", "");
+ static vector<string> v = Tokenize(buildChars, ",");
static bool watch = (std::find(v.begin(), v.end(), "watch") != v.end());
static bool wear = (wearSdkInt > 0) || watch;
static bool logged = false;
@@ -1368,7 +1380,7 @@
// Any released device will have codename REL instead of a 'real' codename.
// For safety: default to 'REL' so we default to unreleased=false on failure.
- const bool unreleased = (base::GetProperty("ro.build.version.codename", "REL") != "REL");
+ const bool unreleased = (GetProperty("ro.build.version.codename", "REL") != "REL");
// goog/main device_api_level is bumped *way* before aosp/main api level
// (the latter only gets bumped during the push of goog/main to aosp/main)
@@ -1397,6 +1409,9 @@
const bool isAtLeastT = (effective_api_level >= __ANDROID_API_T__);
const bool isAtLeastU = (effective_api_level >= __ANDROID_API_U__);
const bool isAtLeastV = (effective_api_level >= __ANDROID_API_V__);
+ const bool isAtLeastW = (effective_api_level > __ANDROID_API_V__); // TODO: switch to W
+
+ const int first_api_level = GetIntProperty("ro.board.first_api_level", effective_api_level);
// last in U QPR2 beta1
const bool has_platform_bpfloader_rc = exists("/system/etc/init/bpfloader.rc");
@@ -1409,6 +1424,7 @@
if (isAtLeastU) ++bpfloader_ver; // [44] BPFLOADER_MAINLINE_U_VERSION
if (runningAsRoot) ++bpfloader_ver; // [45] BPFLOADER_MAINLINE_U_QPR3_VERSION
if (isAtLeastV) ++bpfloader_ver; // [46] BPFLOADER_MAINLINE_V_VERSION
+ if (isAtLeastW) ++bpfloader_ver; // [47] BPFLOADER_MAINLINE_W_VERSION
ALOGI("NetBpfLoad v0.%u (%s) api:%d/%d kver:%07x (%s) uid:%d rc:%d%d",
bpfloader_ver, argv[0], android_get_device_api_level(), effective_api_level,
@@ -1458,6 +1474,12 @@
if (!isTV()) return 1;
}
+ // 6.6 is highest version supported by Android V, so this is effectively W+ (sdk=36+)
+ if (isKernel32Bit() && isAtLeastKernelVersion(6, 7, 0)) {
+ ALOGE("Android platform with 32 bit kernel version >= 6.7.0 is unsupported");
+ return 1;
+ }
+
// Various known ABI layout issues, particularly wrt. bpf and ipsec/xfrm.
if (isAtLeastV && isKernel32Bit() && isX86()) {
ALOGE("Android V requires X86 kernel to be 64-bit.");
@@ -1492,33 +1514,54 @@
}
}
+ /* Android 14/U should only launch on 64-bit kernels
+ * T launches on 5.10/5.15
+ * U launches on 5.15/6.1
+ * So >=5.16 implies isKernel64Bit()
+ *
+ * We thus added a test to V VTS which requires 5.16+ devices to use 64-bit kernels.
+ *
+ * Starting with Android V, which is the first to support a post 6.1 Linux Kernel,
+ * we also require 64-bit userspace.
+ *
+ * There are various known issues with 32-bit userspace talking to various
+ * kernel interfaces (especially CAP_NET_ADMIN ones) on a 64-bit kernel.
+ * Some of these have userspace or kernel workarounds/hacks.
+ * Some of them don't...
+ * We're going to be removing the hacks.
+ * (for example "ANDROID: xfrm: remove in_compat_syscall() checks").
+ * Note: this check/enforcement only applies to *system* userspace code,
+ * it does not affect unprivileged apps, the 32-on-64 compatibility
+ * problems are AFAIK limited to various CAP_NET_ADMIN protected interfaces.
+ *
+ * Additionally the 32-bit kernel jit support is poor,
+ * and 32-bit userspace on 64-bit kernel bpf ringbuffer compatibility is broken.
+ */
if (isUserspace32bit() && isAtLeastKernelVersion(6, 2, 0)) {
- /* Android 14/U should only launch on 64-bit kernels
- * T launches on 5.10/5.15
- * U launches on 5.15/6.1
- * So >=5.16 implies isKernel64Bit()
- *
- * We thus added a test to V VTS which requires 5.16+ devices to use 64-bit kernels.
- *
- * Starting with Android V, which is the first to support a post 6.1 Linux Kernel,
- * we also require 64-bit userspace.
- *
- * There are various known issues with 32-bit userspace talking to various
- * kernel interfaces (especially CAP_NET_ADMIN ones) on a 64-bit kernel.
- * Some of these have userspace or kernel workarounds/hacks.
- * Some of them don't...
- * We're going to be removing the hacks.
- * (for example "ANDROID: xfrm: remove in_compat_syscall() checks").
- * Note: this check/enforcement only applies to *system* userspace code,
- * it does not affect unprivileged apps, the 32-on-64 compatibility
- * problems are AFAIK limited to various CAP_NET_ADMIN protected interfaces.
- *
- * Additionally the 32-bit kernel jit support is poor,
- * and 32-bit userspace on 64-bit kernel bpf ringbuffer compatibility is broken.
- */
- ALOGE("64-bit userspace required on 6.2+ kernels.");
- // Stuff won't work reliably, but exempt TVs & Arm Wear devices
- if (!isTV() && !(isWear() && isArm())) return 1;
+ // Stuff won't work reliably, but...
+ if (isTV()) {
+ // exempt TVs... they don't really need functional advanced networking
+ ALOGW("[TV] 32-bit userspace unsupported on 6.2+ kernels.");
+ } else if (isWear() && isArm()) {
+ // exempt Arm Wear devices (arm32 ABI is far less problematic than x86-32)
+ ALOGW("[Arm Wear] 32-bit userspace unsupported on 6.2+ kernels.");
+ } else if (first_api_level <= __ANDROID_API_T__ && isArm()) {
+ // also exempt Arm devices upgrading with major kernel rev from T-
+ // might possibly be better for them to run with a newer kernel...
+ ALOGW("[Arm KernelUpRev] 32-bit userspace unsupported on 6.2+ kernels.");
+ } else if (isArm()) {
+ ALOGE("[Arm] 64-bit userspace required on 6.2+ kernels (%d).", first_api_level);
+ return 1;
+ } else { // x86 since RiscV cannot be 32-bit
+ ALOGE("[x86] 64-bit userspace required on 6.2+ kernels.");
+ return 1;
+ }
+ }
+
+ // Note: 6.6 is highest version supported by Android V (sdk=35), so this is for sdk=36+
+ if (isUserspace32bit() && isAtLeastKernelVersion(6, 7, 0)) {
+ ALOGE("64-bit userspace required on 6.7+ kernels.");
+ return 1;
}
// Ensure we can determine the Android build type.
@@ -1589,7 +1632,7 @@
int key = 1;
int value = 123;
- base::unique_fd map(
+ unique_fd map(
createMap(BPF_MAP_TYPE_ARRAY, sizeof(key), sizeof(value), 2, 0));
if (writeToMapEntry(map, &key, &value, BPF_ANY)) {
ALOGE("Critical kernel bug - failure to write into index 1 of 2 element bpf map array.");
@@ -1621,11 +1664,11 @@
} // namespace android
int main(int argc, char** argv, char * const envp[]) {
- android::base::InitLogging(argv, &android::base::KernelLogger);
+ InitLogging(argv, &KernelLogger);
if (argc == 2 && !strcmp(argv[1], "done")) {
// we're being re-exec'ed from platform bpfloader to 'finalize' things
- if (!android::base::SetProperty("bpf.progs_loaded", "1")) {
+ if (!SetProperty("bpf.progs_loaded", "1")) {
ALOGE("Failed to set bpf.progs_loaded property to 1.");
return 125;
}
diff --git a/bpf/progs/bpf_net_helpers.h b/bpf/progs/bpf_net_helpers.h
index a86c3e6..a5664ba 100644
--- a/bpf/progs/bpf_net_helpers.h
+++ b/bpf/progs/bpf_net_helpers.h
@@ -139,6 +139,24 @@
if (skb->data_end - skb->data < len) bpf_skb_pull_data(skb, len);
}
+// anti-compiler-optimizer no-op: explicitly force full calculation of 'v'
+//
+// The use for this is to force full calculation of a complex arithmetic (likely binary
+// bitops) value, and then check the result only once (thus likely reducing the number
+// of required conditional jump instructions that badly affect bpf verifier runtime)
+//
+// The compiler cannot look into the assembly statement, so it doesn't know it does nothing.
+// Since the statement takes 'v' as both input and output in a register (+r),
+// the compiler must fully calculate the precise value of 'v' before this,
+// and must use the (possibly modified) value of 'v' afterwards (thus cannot
+// do funky optimizations to use partial results from before the asm).
+//
+// As this is not flagged 'volatile' this may still be moved out of a loop,
+// or even entirely optimized out if 'v' is never used afterwards.
+//
+// See: https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
+#define COMPILER_FORCE_CALCULATION(v) asm ("" : "+r" (v))
+
struct egress_bool { bool egress; };
#define INGRESS ((struct egress_bool){ .egress = false })
#define EGRESS ((struct egress_bool){ .egress = true })
diff --git a/bpf/progs/dscpPolicy.c b/bpf/progs/dscpPolicy.c
index 39f2961..de9723d 100644
--- a/bpf/progs/dscpPolicy.c
+++ b/bpf/progs/dscpPolicy.c
@@ -25,8 +25,8 @@
// The cache is never read nor written by userspace and is indexed by socket cookie % CACHE_MAP_SIZE
#define CACHE_MAP_SIZE 32 // should be a power of two so we can % cheaply
-DEFINE_BPF_MAP_GRO(socket_policy_cache_map, PERCPU_ARRAY, uint32_t, RuleEntry, CACHE_MAP_SIZE,
- AID_SYSTEM)
+DEFINE_BPF_MAP_KERNEL_INTERNAL(socket_policy_cache_map, PERCPU_ARRAY, uint32_t, RuleEntry,
+ CACHE_MAP_SIZE)
DEFINE_BPF_MAP_GRW(ipv4_dscp_policies_map, ARRAY, uint32_t, DscpPolicy, MAX_POLICIES, AID_SYSTEM)
DEFINE_BPF_MAP_GRW(ipv6_dscp_policies_map, ARRAY, uint32_t, DscpPolicy, MAX_POLICIES, AID_SYSTEM)
@@ -113,14 +113,30 @@
// this array lookup cannot actually fail
RuleEntry* existing_rule = bpf_socket_policy_cache_map_lookup_elem(&cacheid);
- if (existing_rule &&
- v6_equal(src_ip, existing_rule->src_ip) &&
- v6_equal(dst_ip, existing_rule->dst_ip) &&
- skb->ifindex == existing_rule->ifindex &&
- sport == existing_rule->src_port &&
- dport == existing_rule->dst_port &&
- protocol == existing_rule->proto) {
- if (existing_rule->dscp_val < 0) return;
+ if (!existing_rule) return; // impossible
+
+ uint64_t nomatch = 0;
+ nomatch |= v6_not_equal(src_ip, existing_rule->src_ip);
+ nomatch |= v6_not_equal(dst_ip, existing_rule->dst_ip);
+ nomatch |= (skb->ifindex ^ existing_rule->ifindex);
+ nomatch |= (sport ^ existing_rule->src_port);
+ nomatch |= (dport ^ existing_rule->dst_port);
+ nomatch |= (protocol ^ existing_rule->proto);
+ COMPILER_FORCE_CALCULATION(nomatch);
+
+ /*
+ * After the above funky bitwise arithmetic we have 'nomatch == 0' iff
+ * src_ip == existing_rule->src_ip &&
+ * dst_ip == existing_rule->dst_ip &&
+ * skb->ifindex == existing_rule->ifindex &&
+ * sport == existing_rule->src_port &&
+ * dport == existing_rule->dst_port &&
+ * protocol == existing_rule->proto
+ */
+
+ if (!nomatch) {
+ if (existing_rule->dscp_val < 0) return; // cached no-op
+
if (ipv4) {
uint8_t newTos = UPDATE_TOS(existing_rule->dscp_val, tos);
bpf_l3_csum_replace(skb, l2_header_size + IP4_OFFSET(check), htons(tos), htons(newTos),
@@ -132,7 +148,7 @@
bpf_skb_store_bytes(skb, l2_header_size, &new_first_be32, sizeof(__be32),
BPF_F_RECOMPUTE_CSUM);
}
- return;
+ return; // cached DSCP mutation
}
// Linear scan ipv4_dscp_policies_map since no stored params match skb.
@@ -187,7 +203,8 @@
}
}
- RuleEntry value = {
+ // Update cache with found policy.
+ *existing_rule = (RuleEntry){
.src_ip = src_ip,
.dst_ip = dst_ip,
.ifindex = skb->ifindex,
@@ -197,9 +214,6 @@
.dscp_val = new_dscp,
};
- // Update cache with found policy.
- bpf_socket_policy_cache_map_update_elem(&cacheid, &value, BPF_ANY);
-
if (new_dscp < 0) return;
// Need to store bytes after updating map or program will not load.
diff --git a/bpf/progs/dscpPolicy.h b/bpf/progs/dscpPolicy.h
index 6a6b711..413fb0f 100644
--- a/bpf/progs/dscpPolicy.h
+++ b/bpf/progs/dscpPolicy.h
@@ -28,9 +28,6 @@
#define v6_not_equal(a, b) ((v6_hi_be64(a) ^ v6_hi_be64(b)) \
| (v6_lo_be64(a) ^ v6_lo_be64(b)))
-// Returns 'a == b' as boolean
-#define v6_equal(a, b) (!v6_not_equal((a), (b)))
-
typedef struct {
struct in6_addr src_ip;
struct in6_addr dst_ip;
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index 63a6cd2..1ebc4a3 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -6734,4 +6734,33 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Get the specified ConnectivityService feature status. This method is for test code to check
+ * whether the feature is enabled or not.
+ * Note that tests can not just read DeviceConfig since ConnectivityService reads flag at
+ * startup. For example, it's possible that the current flag value is "disable"(-1) but the
+ * feature is enabled since the flag value was "enable"(1) when ConnectivityService started up.
+ * If the ConnectivityManager needs to check the ConnectivityService feature status for non-test
+ * purpose, define feature in {@link ConnectivityManagerFeature} and use
+ * {@link #isFeatureEnabled} instead.
+ *
+ * @param featureFlag target flag for feature
+ * @return {@code true} if the feature is enabled, {@code false} if the feature is disabled.
+ * @throws IllegalArgumentException if the flag is invalid
+ *
+ * @hide
+ */
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK
+ })
+ public boolean isConnectivityServiceFeatureEnabledForTesting(final String featureFlag) {
+ try {
+ return mService.isConnectivityServiceFeatureEnabledForTesting(featureFlag);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
}
diff --git a/framework/src/android/net/IConnectivityManager.aidl b/framework/src/android/net/IConnectivityManager.aidl
index 988cc92..47b3316 100644
--- a/framework/src/android/net/IConnectivityManager.aidl
+++ b/framework/src/android/net/IConnectivityManager.aidl
@@ -262,4 +262,6 @@
IBinder getRoutingCoordinatorService();
long getEnabledConnectivityManagerFeatures();
+
+ boolean isConnectivityServiceFeatureEnabledForTesting(String featureFlag);
}
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 9af250b..990f43e 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -14505,4 +14505,14 @@
}
return features;
}
+
+ @Override
+ public boolean isConnectivityServiceFeatureEnabledForTesting(String featureFlag) {
+ switch (featureFlag) {
+ case INGRESS_TO_VPN_ADDRESS_FILTERING:
+ return mIngressToVpnAddressFiltering;
+ default:
+ throw new IllegalArgumentException("Unknown flag: " + featureFlag);
+ }
+ }
}
diff --git a/service/src/com/android/server/connectivity/DscpPolicyValue.java b/service/src/com/android/server/connectivity/DscpPolicyValue.java
index 7162a4a..a9100ac 100644
--- a/service/src/com/android/server/connectivity/DscpPolicyValue.java
+++ b/service/src/com/android/server/connectivity/DscpPolicyValue.java
@@ -117,8 +117,8 @@
this.proto = proto != -1 ? proto : 0;
this.dscp = dscp;
- this.match_src_ip = (this.src46 != EMPTY_ADDRESS_FIELD);
- this.match_dst_ip = (this.dst46 != EMPTY_ADDRESS_FIELD);
+ this.match_src_ip = (src46 != null);
+ this.match_dst_ip = (dst46 != null);
this.match_src_port = (srcPort != -1);
this.match_proto = (proto != -1);
}
diff --git a/staticlibs/framework/com/android/net/module/util/LocationPermissionChecker.java b/staticlibs/framework/com/android/net/module/util/LocationPermissionChecker.java
index 28c33f3..e4d25cd 100644
--- a/staticlibs/framework/com/android/net/module/util/LocationPermissionChecker.java
+++ b/staticlibs/framework/com/android/net/module/util/LocationPermissionChecker.java
@@ -117,7 +117,11 @@
@VisibleForTesting(visibility = PRIVATE)
public @LocationPermissionCheckStatus int checkLocationPermissionInternal(
String pkgName, @Nullable String featureId, int uid, @Nullable String message) {
- checkPackage(uid, pkgName);
+ try {
+ checkPackage(uid, pkgName);
+ } catch (SecurityException e) {
+ return ERROR_LOCATION_PERMISSION_MISSING;
+ }
// Apps with NETWORK_SETTINGS, NETWORK_SETUP_WIZARD, NETWORK_STACK & MAINLINE_NETWORK_STACK
// are granted a bypass.
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/LocationPermissionCheckerTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/LocationPermissionCheckerTest.java
index c8f8656..d773374 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/LocationPermissionCheckerTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/LocationPermissionCheckerTest.java
@@ -18,7 +18,6 @@
import static android.Manifest.permission.NETWORK_SETTINGS;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
@@ -47,7 +46,6 @@
import com.android.testutils.DevSdkIgnoreRule;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -242,9 +240,9 @@
mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
setupTestCase();
- assertThrows(SecurityException.class,
- () -> mChecker.checkLocationPermissionInternal(
- TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
+ final int result = mChecker.checkLocationPermissionInternal(
+ TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
+ assertEquals(LocationPermissionChecker.ERROR_LOCATION_PERMISSION_MISSING, result);
}
@Test
@@ -305,14 +303,4 @@
TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
assertEquals(LocationPermissionChecker.SUCCEEDED, result);
}
-
-
- private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
- try {
- r.run();
- Assert.fail("Expected " + exceptionClass + " to be thrown.");
- } catch (Exception exception) {
- assertTrue(exceptionClass.isInstance(exception));
- }
- }
}
diff --git a/tests/cts/hostside/AndroidTest.xml b/tests/cts/hostside/AndroidTest.xml
index ea6b078..03ea178 100644
--- a/tests/cts/hostside/AndroidTest.xml
+++ b/tests/cts/hostside/AndroidTest.xml
@@ -20,6 +20,9 @@
<option name="config-descriptor:metadata" key="parameter" value="instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="mainline-param" value="CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk+com.google.android.resolv.apex+com.google.android.tethering.apex" />
+ <option name="config-descriptor:metadata" key="mainline-param" value="CaptivePortalLoginGoogle.apk+NetworkStackGoogle.apk" />
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.tethering.apex" />
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.LocationCheck" />
@@ -46,4 +49,21 @@
<option name="directory-keys" value="/sdcard/CtsHostsideNetworkTests" />
<option name="collect-on-run-ended-only" value="true" />
</metrics_collector>
+
+<!-- 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/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index e186c6b..2db1db5 100755
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -213,6 +213,8 @@
private static final String AUTOMATIC_ON_OFF_KEEPALIVE_VERSION =
"automatic_on_off_keepalive_version";
+ private static final String INGRESS_TO_VPN_ADDRESS_FILTERING =
+ "ingress_to_vpn_address_filtering";
// Enabled since version 1 means it's always enabled because the version is always above 1
private static final String AUTOMATIC_ON_OFF_KEEPALIVE_ENABLED = "1";
private static final long TEST_TCP_POLLING_TIMER_EXPIRED_PERIOD_MS = 60_000L;
@@ -1949,6 +1951,9 @@
*/
private void doTestDropPacketToVpnAddress(final boolean duplicatedAddress)
throws Exception {
+ assumeTrue(mCM.isConnectivityServiceFeatureEnabledForTesting(
+ INGRESS_TO_VPN_ADDRESS_FILTERING));
+
final NetworkRequest request = new NetworkRequest.Builder()
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED)
diff --git a/tests/cts/hostside/app2/Android.bp b/tests/cts/hostside/app2/Android.bp
index 12ea23b..3dde3ff 100644
--- a/tests/cts/hostside/app2/Android.bp
+++ b/tests/cts/hostside/app2/Android.bp
@@ -36,4 +36,5 @@
"sts",
],
sdk_version: "test_current",
+ min_sdk_version: "31",
}
diff --git a/tests/cts/hostside/networkslicingtestapp/Android.bp b/tests/cts/hostside/networkslicingtestapp/Android.bp
index c220000..333195c 100644
--- a/tests/cts/hostside/networkslicingtestapp/Android.bp
+++ b/tests/cts/hostside/networkslicingtestapp/Android.bp
@@ -35,6 +35,7 @@
"general-tests",
"sts",
],
+ min_sdk_version: "31",
}
android_test_helper_app {
diff --git a/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt b/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
index f73134a..041e6cb 100644
--- a/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
+++ b/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
@@ -298,7 +298,8 @@
fun sendPacket(
agent: TestableNetworkAgent,
sendV6: Boolean,
- dstPort: Int = 0
+ dstPort: Int = 0,
+ times: Int = 1
) {
val testString = "test string"
val testPacket = ByteBuffer.wrap(testString.toByteArray(Charsets.UTF_8))
@@ -308,9 +309,11 @@
IPPROTO_UDP)
checkNotNull(agent.network).bindSocket(socket)
- val originalPacket = testPacket.readAsArray()
- Os.sendto(socket, originalPacket, 0 /* bytesOffset */, originalPacket.size, 0 /* flags */,
+ val origPacket = testPacket.readAsArray()
+ repeat(times) {
+ Os.sendto(socket, origPacket, 0 /* bytesOffset */, origPacket.size, 0 /* flags */,
if (sendV6) TEST_TARGET_IPV6_ADDR else TEST_TARGET_IPV4_ADDR, dstPort)
+ }
Os.close(socket)
}
@@ -400,10 +403,11 @@
agent: TestableNetworkAgent,
sendV6: Boolean = false,
dscpValue: Int = 0,
- dstPort: Int = 0
+ dstPort: Int = 0,
+ times: Int = 1
) {
- var packetFound = false
- sendPacket(agent, sendV6, dstPort)
+ var packetFound = 0
+ sendPacket(agent, sendV6, dstPort, times)
// TODO: grab source port from socket in sendPacket
Log.e(TAG, "find DSCP value:" + dscpValue)
@@ -424,10 +428,23 @@
if (parsePacketIp(buffer, sendV6) && parsePacketPort(buffer, 0, dstPort)) {
Log.e(TAG, "DSCP value found")
assertEquals(dscpValue, dscp)
- packetFound = true
+ packetFound++
}
}
- assertTrue(packetFound)
+ assertTrue(packetFound == times)
+ }
+
+ fun validatePackets(
+ agent: TestableNetworkAgent,
+ sendV6: Boolean = false,
+ dscpValue: Int = 0,
+ dstPort: Int = 0
+ ) {
+ // We send two packets from the same socket to verify
+ // socket caching works correctly.
+ validatePacket(agent, sendV6, dscpValue, dstPort, 2)
+ // Try one more time from a different socket.
+ validatePacket(agent, sendV6, dscpValue, dstPort, 1)
}
fun doRemovePolicyTest(
@@ -453,10 +470,7 @@
assertEquals(1, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
}
- validatePacket(agent, dscpValue = 1, dstPort = 4444)
- // Send a second packet to validate that the stored BPF policy
- // is correct for subsequent packets.
- validatePacket(agent, dscpValue = 1, dstPort = 4444)
+ validatePackets(agent, dscpValue = 1, dstPort = 4444)
agent.sendRemoveDscpPolicy(1)
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
@@ -475,7 +489,7 @@
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
}
- validatePacket(agent, dscpValue = 4, dstPort = 5555)
+ validatePackets(agent, dscpValue = 4, dstPort = 5555)
agent.sendRemoveDscpPolicy(1)
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
@@ -494,10 +508,7 @@
assertEquals(1, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
}
- validatePacket(agent, true, dscpValue = 1, dstPort = 4444)
- // Send a second packet to validate that the stored BPF policy
- // is correct for subsequent packets.
- validatePacket(agent, true, dscpValue = 1, dstPort = 4444)
+ validatePackets(agent, true, dscpValue = 1, dstPort = 4444)
agent.sendRemoveDscpPolicy(1)
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
@@ -515,7 +526,7 @@
assertEquals(1, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
}
- validatePacket(agent, true, dscpValue = 4, dstPort = 5555)
+ validatePackets(agent, true, dscpValue = 4, dstPort = 5555)
agent.sendRemoveDscpPolicy(1)
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
@@ -533,7 +544,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(1, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- validatePacket(agent, dscpValue = 1, dstPort = 1111)
+ validatePackets(agent, dscpValue = 1, dstPort = 1111)
}
val policy2 = DscpPolicy.Builder(2, 1).setDestinationPortRange(Range(2222, 2222)).build()
@@ -541,7 +552,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(2, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- validatePacket(agent, dscpValue = 1, dstPort = 2222)
+ validatePackets(agent, dscpValue = 1, dstPort = 2222)
}
val policy3 = DscpPolicy.Builder(3, 1).setDestinationPortRange(Range(3333, 3333)).build()
@@ -549,16 +560,16 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(3, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- validatePacket(agent, dscpValue = 1, dstPort = 3333)
+ validatePackets(agent, dscpValue = 1, dstPort = 3333)
}
/* Remove Policies and check CE is no longer set */
doRemovePolicyTest(agent, callback, 1)
- validatePacket(agent, dscpValue = 0, dstPort = 1111)
+ validatePackets(agent, dscpValue = 0, dstPort = 1111)
doRemovePolicyTest(agent, callback, 2)
- validatePacket(agent, dscpValue = 0, dstPort = 2222)
+ validatePackets(agent, dscpValue = 0, dstPort = 2222)
doRemovePolicyTest(agent, callback, 3)
- validatePacket(agent, dscpValue = 0, dstPort = 3333)
+ validatePackets(agent, dscpValue = 0, dstPort = 3333)
}
@Test
@@ -569,7 +580,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(1, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- validatePacket(agent, dscpValue = 1, dstPort = 1111)
+ validatePackets(agent, dscpValue = 1, dstPort = 1111)
}
doRemovePolicyTest(agent, callback, 1)
@@ -578,7 +589,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(2, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- validatePacket(agent, dscpValue = 1, dstPort = 2222)
+ validatePackets(agent, dscpValue = 1, dstPort = 2222)
}
doRemovePolicyTest(agent, callback, 2)
@@ -587,7 +598,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(3, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- validatePacket(agent, dscpValue = 1, dstPort = 3333)
+ validatePackets(agent, dscpValue = 1, dstPort = 3333)
}
doRemovePolicyTest(agent, callback, 3)
}
@@ -601,7 +612,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(1, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- validatePacket(agent, dscpValue = 1, dstPort = 1111)
+ validatePackets(agent, dscpValue = 1, dstPort = 1111)
}
val policy2 = DscpPolicy.Builder(2, 1).setDestinationPortRange(Range(2222, 2222)).build()
@@ -609,7 +620,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(2, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- validatePacket(agent, dscpValue = 1, dstPort = 2222)
+ validatePackets(agent, dscpValue = 1, dstPort = 2222)
}
val policy3 = DscpPolicy.Builder(3, 1).setDestinationPortRange(Range(3333, 3333)).build()
@@ -617,7 +628,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(3, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- validatePacket(agent, dscpValue = 1, dstPort = 3333)
+ validatePackets(agent, dscpValue = 1, dstPort = 3333)
}
/* Remove Policies and check CE is no longer set */
@@ -643,7 +654,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(1, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- validatePacket(agent, dscpValue = 1, dstPort = 1111)
+ validatePackets(agent, dscpValue = 1, dstPort = 1111)
}
val policy2 = DscpPolicy.Builder(2, 1)
@@ -652,7 +663,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(2, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- validatePacket(agent, dscpValue = 1, dstPort = 2222)
+ validatePackets(agent, dscpValue = 1, dstPort = 2222)
}
val policy3 = DscpPolicy.Builder(3, 1)
@@ -661,24 +672,24 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(3, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- validatePacket(agent, dscpValue = 1, dstPort = 3333)
+ validatePackets(agent, dscpValue = 1, dstPort = 3333)
}
agent.sendRemoveAllDscpPolicies()
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(1, it.policyId)
assertEquals(DSCP_POLICY_STATUS_DELETED, it.status)
- validatePacket(agent, false, dstPort = 1111)
+ validatePackets(agent, false, dstPort = 1111)
}
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(2, it.policyId)
assertEquals(DSCP_POLICY_STATUS_DELETED, it.status)
- validatePacket(agent, false, dstPort = 2222)
+ validatePackets(agent, false, dstPort = 2222)
}
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(3, it.policyId)
assertEquals(DSCP_POLICY_STATUS_DELETED, it.status)
- validatePacket(agent, false, dstPort = 3333)
+ validatePackets(agent, false, dstPort = 3333)
}
}
@@ -690,7 +701,7 @@
agent.expectCallback<OnDscpPolicyStatusUpdated>().let {
assertEquals(1, it.policyId)
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
- validatePacket(agent, dscpValue = 1, dstPort = 4444)
+ validatePackets(agent, dscpValue = 1, dstPort = 4444)
}
val policy2 = DscpPolicy.Builder(1, 1).setDestinationPortRange(Range(5555, 5555)).build()
@@ -700,8 +711,8 @@
assertEquals(DSCP_POLICY_STATUS_SUCCESS, it.status)
// Sending packet with old policy should fail
- validatePacket(agent, dscpValue = 0, dstPort = 4444)
- validatePacket(agent, dscpValue = 1, dstPort = 5555)
+ validatePackets(agent, dscpValue = 0, dstPort = 4444)
+ validatePackets(agent, dscpValue = 1, dstPort = 5555)
}
agent.sendRemoveDscpPolicy(1)
diff --git a/tests/cts/net/src/android/net/cts/IpSecBaseTest.java b/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
index 2a6c638..c480135 100644
--- a/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
@@ -23,10 +23,13 @@
import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA384;
import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA512;
import static android.net.IpSecAlgorithm.CRYPT_AES_CBC;
+import static android.net.cts.PacketUtils.ICMP_HDRLEN;
+import static android.net.cts.PacketUtils.IP6_HDRLEN;
import static android.system.OsConstants.FIONREAD;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import android.content.Context;
import android.net.ConnectivityManager;
@@ -152,10 +155,17 @@
final IpSecTransformState transformState =
futureIpSecTransform.get(SOCK_TIMEOUT, TimeUnit.MILLISECONDS);
- assertEquals(txHighestSeqNum, transformState.getTxHighestSequenceNumber());
+ // There might be ICMPv6(Router Solicitation) packets. Thus we can only check the lower
+ // bound of the outgoing traffic.
+ final long icmpV6RsCnt = transformState.getTxHighestSequenceNumber() - txHighestSeqNum;
+ assertTrue(icmpV6RsCnt >= 0);
+
+ final long adjustedPacketCnt = packetCnt + icmpV6RsCnt;
+ final long adjustedByteCnt = byteCnt + icmpV6RsCnt * (IP6_HDRLEN + ICMP_HDRLEN);
+
+ assertEquals(adjustedPacketCnt, transformState.getPacketCount());
+ assertEquals(adjustedByteCnt, transformState.getByteCount());
assertEquals(rxHighestSeqNum, transformState.getRxHighestSequenceNumber());
- assertEquals(packetCnt, transformState.getPacketCount());
- assertEquals(byteCnt, transformState.getByteCount());
assertArrayEquals(replayBitmap, transformState.getReplayBitmap());
}
diff --git a/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java b/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java
index 22a51d6..890c071 100644
--- a/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java
@@ -65,6 +65,7 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import com.android.net.module.util.CollectionUtils;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
@@ -119,7 +120,7 @@
private static final int TIMEOUT_MS = 500;
- private static final int PACKET_COUNT = 5000;
+ private static final int PACKET_COUNT = 100;
// Static state to reduce setup/teardown
private static ConnectivityManager sCM;
@@ -1088,6 +1089,27 @@
UdpEncapsulationSocket encapSocket,
IpSecTunnelTestRunnable test)
throws Exception {
+ return buildTunnelNetworkAndRunTests(
+ localInner,
+ remoteInner,
+ localOuter,
+ remoteOuter,
+ spi,
+ encapSocket,
+ test,
+ true /* enableEncrypt */);
+ }
+
+ private int buildTunnelNetworkAndRunTests(
+ InetAddress localInner,
+ InetAddress remoteInner,
+ InetAddress localOuter,
+ InetAddress remoteOuter,
+ int spi,
+ UdpEncapsulationSocket encapSocket,
+ IpSecTunnelTestRunnable test,
+ boolean enableEncrypt)
+ throws Exception {
int innerPrefixLen = localInner instanceof Inet6Address ? IP6_PREFIX_LEN : IP4_PREFIX_LEN;
TestNetworkCallback testNetworkCb = null;
int innerSocketPort;
@@ -1115,8 +1137,12 @@
// Configure Transform parameters
IpSecTransform.Builder transformBuilder = new IpSecTransform.Builder(sContext);
- transformBuilder.setEncryption(
- new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY));
+
+ if (enableEncrypt) {
+ transformBuilder.setEncryption(
+ new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY));
+ }
+
transformBuilder.setAuthentication(
new IpSecAlgorithm(
IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4));
@@ -1167,8 +1193,8 @@
return innerSocketPort;
}
- private int buildTunnelNetworkAndRunTestsSimple(int spi, IpSecTunnelTestRunnable test)
- throws Exception {
+ private int buildTunnelNetworkAndRunTestsSimple(
+ int spi, IpSecTunnelTestRunnable test, boolean enableEncrypt) throws Exception {
return buildTunnelNetworkAndRunTests(
LOCAL_INNER_6,
REMOTE_INNER_6,
@@ -1176,7 +1202,8 @@
REMOTE_OUTER_6,
spi,
null /* encapSocket */,
- test);
+ test,
+ enableEncrypt);
}
private static void receiveAndValidatePacket(JavaUdpSocket socket) throws Exception {
@@ -1787,10 +1814,11 @@
PACKET_COUNT,
PACKET_COUNT,
PACKET_COUNT * (long) innerPacketSize,
- newReplayBitmap(REPLAY_BITMAP_LEN_BYTE * 8));
+ newReplayBitmap(PACKET_COUNT));
return innerSocketPort;
- });
+ },
+ true /* enableEncrypt */);
}
@IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@@ -1814,17 +1842,22 @@
ipsecNetwork.bindSocket(outSocket.mSocket);
int innerSocketPort = outSocket.getPort();
- int expectedPacketSize =
- getPacketSize(
- AF_INET6,
- AF_INET6,
- false /* useEncap */,
- false /* transportInTunnelMode */);
+ int outSeqNum = 1;
+ int receivedTestDataEspCnt = 0;
- for (int i = 0; i < PACKET_COUNT; i++) {
+ while (receivedTestDataEspCnt < PACKET_COUNT) {
outSocket.sendTo(TEST_DATA, REMOTE_INNER_6, innerSocketPort);
- tunUtils.awaitEspPacketNoPlaintext(
- spi, TEST_DATA, false /* useEncap */, expectedPacketSize);
+
+ byte[] pkt = null;
+
+ // If it is an ESP that contains the TEST_DATA, move to the next
+ // loop. Otherwise, the ESP may contain an ICMPv6(Router Solicitation).
+ // In this case, just increase the expected sequence number and continue
+ // waiting for the ESP with TEST_DATA
+ do {
+ pkt = tunUtils.awaitEspPacket(spi, false /* useEncap */, outSeqNum++);
+ } while (CollectionUtils.indexOfSubArray(pkt, TEST_DATA) == -1);
+ receivedTestDataEspCnt++;
}
final int innerPacketSize =
@@ -1838,6 +1871,7 @@
newReplayBitmap(0));
return innerSocketPort;
- });
+ },
+ false /* enableEncrypt */);
}
}
diff --git a/tests/cts/net/src/android/net/cts/PacketUtils.java b/tests/cts/net/src/android/net/cts/PacketUtils.java
index 4d924d1..1588835 100644
--- a/tests/cts/net/src/android/net/cts/PacketUtils.java
+++ b/tests/cts/net/src/android/net/cts/PacketUtils.java
@@ -49,6 +49,7 @@
static final int TCP_HDRLEN = 20;
static final int TCP_HDRLEN_WITH_TIMESTAMP_OPT = TCP_HDRLEN + 12;
static final int ESP_HDRLEN = 8;
+ static final int ICMP_HDRLEN = 8;
static final int ESP_BLK_SIZE = 4; // ESP has to be 4-byte aligned
static final int ESP_TRAILER_LEN = 2;
diff --git a/tests/cts/net/src/android/net/cts/TunUtils.java b/tests/cts/net/src/android/net/cts/TunUtils.java
index 268d8d2..8bf4998 100644
--- a/tests/cts/net/src/android/net/cts/TunUtils.java
+++ b/tests/cts/net/src/android/net/cts/TunUtils.java
@@ -47,6 +47,8 @@
protected static final int IP4_PROTO_OFFSET = 9;
protected static final int IP6_PROTO_OFFSET = 6;
+ private static final int SEQ_NUM_MATCH_NOT_REQUIRED = -1;
+
private static final int DATA_BUFFER_LEN = 4096;
private static final int TIMEOUT = 2000;
@@ -146,16 +148,30 @@
return espPkt; // We've found the packet we're looking for.
}
+ /** Await the expected ESP packet */
public byte[] awaitEspPacket(int spi, boolean useEncap) throws Exception {
- return awaitPacket((pkt) -> isEsp(pkt, spi, useEncap));
+ return awaitEspPacket(spi, useEncap, SEQ_NUM_MATCH_NOT_REQUIRED);
}
- private static boolean isSpiEqual(byte[] pkt, int espOffset, int spi) {
+ /** Await the expected ESP packet with a matching sequence number */
+ public byte[] awaitEspPacket(int spi, boolean useEncap, int seqNum) throws Exception {
+ return awaitPacket((pkt) -> isEsp(pkt, spi, seqNum, useEncap));
+ }
+
+ private static boolean isMatchingEspPacket(byte[] pkt, int espOffset, int spi, int seqNum) {
ByteBuffer buffer = ByteBuffer.wrap(pkt);
buffer.get(new byte[espOffset]); // Skip IP, UDP header
int actualSpi = buffer.getInt();
+ int actualSeqNum = buffer.getInt();
- return actualSpi == spi;
+ if (actualSeqNum < 0) {
+ throw new UnsupportedOperationException(
+ "actualSeqNum overflowed and needs to be converted to an unsigned integer");
+ }
+
+ boolean isSeqNumMatched = (seqNum == SEQ_NUM_MATCH_NOT_REQUIRED || seqNum == actualSeqNum);
+
+ return actualSpi == spi && isSeqNumMatched;
}
/**
@@ -173,29 +189,32 @@
fail("Banned plaintext packet found");
}
- return isEsp(pkt, spi, encap);
+ return isEsp(pkt, spi, SEQ_NUM_MATCH_NOT_REQUIRED, encap);
}
- private static boolean isEsp(byte[] pkt, int spi, boolean encap) {
+ private static boolean isEsp(byte[] pkt, int spi, int seqNum, boolean encap) {
if (isIpv6(pkt)) {
if (encap) {
return pkt[IP6_PROTO_OFFSET] == IPPROTO_UDP
- && isSpiEqual(pkt, IP6_HDRLEN + UDP_HDRLEN, spi);
+ && isMatchingEspPacket(pkt, IP6_HDRLEN + UDP_HDRLEN, spi, seqNum);
} else {
- return pkt[IP6_PROTO_OFFSET] == IPPROTO_ESP && isSpiEqual(pkt, IP6_HDRLEN, spi);
+ return pkt[IP6_PROTO_OFFSET] == IPPROTO_ESP
+ && isMatchingEspPacket(pkt, IP6_HDRLEN, spi, seqNum);
}
} else {
// Use default IPv4 header length (assuming no options)
if (encap) {
return pkt[IP4_PROTO_OFFSET] == IPPROTO_UDP
- && isSpiEqual(pkt, IP4_HDRLEN + UDP_HDRLEN, spi);
+ && isMatchingEspPacket(pkt, IP4_HDRLEN + UDP_HDRLEN, spi, seqNum);
} else {
- return pkt[IP4_PROTO_OFFSET] == IPPROTO_ESP && isSpiEqual(pkt, IP4_HDRLEN, spi);
+ return pkt[IP4_PROTO_OFFSET] == IPPROTO_ESP
+ && isMatchingEspPacket(pkt, IP4_HDRLEN, spi, seqNum);
}
}
}
+
public static boolean isIpv6(byte[] pkt) {
// First nibble shows IP version. 0x60 for IPv6
return (pkt[0] & (byte) 0xF0) == (byte) 0x60;
diff --git a/tests/unit/java/android/net/IpMemoryStoreTest.java b/tests/unit/java/android/net/IpMemoryStoreTest.java
index 0b82759..e8f91e6 100644
--- a/tests/unit/java/android/net/IpMemoryStoreTest.java
+++ b/tests/unit/java/android/net/IpMemoryStoreTest.java
@@ -16,6 +16,11 @@
package android.net;
+import static android.net.IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ROAM;
+import static android.net.IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_CONFIRM;
+import static android.net.IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_ORGANIC;
+import static android.net.IIpMemoryStore.NETWORK_EVENT_NUD_FAILURE_MAC_ADDRESS_CHANGED;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
@@ -68,6 +73,14 @@
-128, 0, 89, 112, 91, -34 };
private static final NetworkAttributes TEST_NETWORK_ATTRIBUTES = buildTestNetworkAttributes(
"hint", 219);
+ private static final long ONE_WEEK_IN_MS = 7 * 24 * 3600 * 1000;
+ private static final long ONE_DAY_IN_MS = 24 * 3600 * 1000;
+ private static final int[] NETWORK_EVENT_NUD_FAILURES = new int[] {
+ NETWORK_EVENT_NUD_FAILURE_ROAM,
+ NETWORK_EVENT_NUD_FAILURE_CONFIRM,
+ NETWORK_EVENT_NUD_FAILURE_ORGANIC,
+ NETWORK_EVENT_NUD_FAILURE_MAC_ADDRESS_CHANGED
+ };
@Mock
Context mMockContext;
@@ -333,4 +346,31 @@
mStore.factoryReset();
verify(mMockService, times(1)).factoryReset();
}
+
+ @Test
+ public void testNetworkEvents() throws Exception {
+ startIpMemoryStore(true /* supplyService */);
+ final String cluster = "cluster";
+
+ final long now = System.currentTimeMillis();
+ final long expiry = now + ONE_WEEK_IN_MS;
+ mStore.storeNetworkEvent(cluster, now, expiry, NETWORK_EVENT_NUD_FAILURE_ROAM,
+ status -> assertTrue("Store not successful : " + status.resultCode,
+ status.isSuccess()));
+ verify(mMockService, times(1)).storeNetworkEvent(eq(cluster),
+ eq(now), eq(expiry), eq(NETWORK_EVENT_NUD_FAILURE_ROAM), any());
+
+ final long[] sinceTimes = new long[2];
+ sinceTimes[0] = now - ONE_WEEK_IN_MS;
+ sinceTimes[1] = now - ONE_DAY_IN_MS;
+ mStore.retrieveNetworkEventCount(cluster, sinceTimes, NETWORK_EVENT_NUD_FAILURES,
+ (status, counts) -> {
+ assertTrue("Retrieve network event counts not successful : "
+ + status.resultCode, status.isSuccess());
+ assertEquals(new int[0], counts);
+ });
+
+ verify(mMockService, times(1)).retrieveNetworkEventCount(eq(cluster), eq(sinceTimes),
+ eq(NETWORK_EVENT_NUD_FAILURES), any());
+ }
}
diff --git a/thread/apex/Android.bp b/thread/apex/Android.bp
index edf000a..838c0d9 100644
--- a/thread/apex/Android.bp
+++ b/thread/apex/Android.bp
@@ -23,8 +23,8 @@
// See https://android.googlesource.com/platform/system/core/+/HEAD/init/README.md#versioned-rc-files-within-apexs
// for details of versioned rc files.
prebuilt_etc {
- name: "ot-daemon.init.34rc",
+ name: "ot-daemon.34rc",
src: "ot-daemon.34rc",
- filename: "init.34rc",
+ filename: "ot-daemon.34rc",
installable: false,
}
diff --git a/thread/service/java/com/android/server/thread/NsdPublisher.java b/thread/service/java/com/android/server/thread/NsdPublisher.java
index 1447ff8..8d89e13 100644
--- a/thread/service/java/com/android/server/thread/NsdPublisher.java
+++ b/thread/service/java/com/android/server/thread/NsdPublisher.java
@@ -31,10 +31,10 @@
import android.os.Handler;
import android.os.RemoteException;
import android.text.TextUtils;
-import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.SharedLog;
import com.android.server.thread.openthread.DnsTxtAttribute;
import com.android.server.thread.openthread.INsdDiscoverServiceCallback;
import com.android.server.thread.openthread.INsdPublisher;
@@ -62,6 +62,7 @@
*/
public final class NsdPublisher extends INsdPublisher.Stub {
private static final String TAG = NsdPublisher.class.getSimpleName();
+ private static final SharedLog LOG = ThreadNetworkLogger.forSubComponent(TAG);
// TODO: b/321883491 - specify network for mDNS operations
@Nullable private Network mNetwork;
@@ -158,8 +159,7 @@
int listenerId,
String registrationType) {
checkOnHandlerThread();
- Log.i(
- TAG,
+ LOG.i(
"Registering "
+ registrationType
+ ". Listener ID: "
@@ -171,7 +171,7 @@
try {
mNsdManager.registerService(serviceInfo, PROTOCOL_DNS_SD, mExecutor, listener);
} catch (IllegalArgumentException e) {
- Log.i(TAG, "Failed to register service. serviceInfo: " + serviceInfo, e);
+ LOG.e("Failed to register service. serviceInfo: " + serviceInfo, e);
listener.onRegistrationFailed(serviceInfo, NsdManager.FAILURE_INTERNAL_ERROR);
}
}
@@ -184,8 +184,7 @@
checkOnHandlerThread();
RegistrationListener registrationListener = mRegistrationListeners.get(listenerId);
if (registrationListener == null) {
- Log.w(
- TAG,
+ LOG.w(
"Failed to unregister service."
+ " Listener ID: "
+ listenerId
@@ -193,8 +192,7 @@
return;
}
- Log.i(
- TAG,
+ LOG.i(
"Unregistering service."
+ " Listener ID: "
+ listenerId
@@ -212,13 +210,7 @@
private void discoverServiceInternal(
String type, INsdDiscoverServiceCallback callback, int listenerId) {
checkOnHandlerThread();
- Log.i(
- TAG,
- "Discovering services."
- + " Listener ID: "
- + listenerId
- + ", service type: "
- + type);
+ LOG.i("Discovering services." + " Listener ID: " + listenerId + ", service type: " + type);
DiscoveryListener listener = new DiscoveryListener(listenerId, type, callback);
mDiscoveryListeners.append(listenerId, listener);
@@ -237,15 +229,14 @@
DiscoveryListener listener = mDiscoveryListeners.get(listenerId);
if (listener == null) {
- Log.w(
- TAG,
+ LOG.w(
"Failed to stop service discovery. Listener ID "
+ listenerId
+ ". The listener is null.");
return;
}
- Log.i(TAG, "Stopping service discovery. Listener: " + listener);
+ LOG.i("Stopping service discovery. Listener: " + listener);
mNsdManager.stopServiceDiscovery(listener);
}
@@ -263,8 +254,7 @@
serviceInfo.setServiceName(name);
serviceInfo.setServiceType(type);
serviceInfo.setNetwork(null);
- Log.i(
- TAG,
+ LOG.i(
"Resolving service."
+ " Listener ID: "
+ listenerId
@@ -288,21 +278,19 @@
ServiceInfoListener listener = mServiceInfoListeners.get(listenerId);
if (listener == null) {
- Log.w(
- TAG,
+ LOG.w(
"Failed to stop service resolution. Listener ID: "
+ listenerId
+ ". The listener is null.");
return;
}
- Log.i(TAG, "Stopping service resolution. Listener: " + listener);
+ LOG.i("Stopping service resolution. Listener: " + listener);
try {
mNsdManager.unregisterServiceInfoCallback(listener);
} catch (IllegalArgumentException e) {
- Log.w(
- TAG,
+ LOG.w(
"Failed to stop the service resolution because it's already stopped. Listener: "
+ listener);
}
@@ -330,7 +318,7 @@
listener);
mHostInfoListeners.append(listenerId, listener);
- Log.i(TAG, "Resolving host." + " Listener ID: " + listenerId + ", hostname: " + name);
+ LOG.i("Resolving host." + " Listener ID: " + listenerId + ", hostname: " + name);
}
@Override
@@ -343,14 +331,13 @@
HostInfoListener listener = mHostInfoListeners.get(listenerId);
if (listener == null) {
- Log.w(
- TAG,
+ LOG.w(
"Failed to stop host resolution. Listener ID: "
+ listenerId
+ ". The listener is null.");
return;
}
- Log.i(TAG, "Stopping host resolution. Listener: " + listener);
+ LOG.i("Stopping host resolution. Listener: " + listener);
listener.cancel();
mHostInfoListeners.remove(listenerId);
}
@@ -373,14 +360,14 @@
try {
mNsdManager.unregisterService(mRegistrationListeners.valueAt(i));
} catch (IllegalArgumentException e) {
- Log.i(
- TAG,
+ LOG.i(
"Failed to unregister."
+ " Listener ID: "
+ mRegistrationListeners.keyAt(i)
+ " serviceInfo: "
- + mRegistrationListeners.valueAt(i).mServiceInfo,
- e);
+ + mRegistrationListeners.valueAt(i).mServiceInfo
+ + ", error: "
+ + e.getMessage());
}
}
mRegistrationListeners.clear();
@@ -415,8 +402,7 @@
public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
checkOnHandlerThread();
mRegistrationListeners.remove(mListenerId);
- Log.i(
- TAG,
+ LOG.i(
"Failed to register listener ID: "
+ mListenerId
+ " error code: "
@@ -434,8 +420,7 @@
public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
checkOnHandlerThread();
for (INsdStatusReceiver receiver : mUnregistrationReceivers) {
- Log.i(
- TAG,
+ LOG.i(
"Failed to unregister."
+ "Listener ID: "
+ mListenerId
@@ -454,8 +439,7 @@
@Override
public void onServiceRegistered(NsdServiceInfo serviceInfo) {
checkOnHandlerThread();
- Log.i(
- TAG,
+ LOG.i(
"Registered successfully. "
+ "Listener ID: "
+ mListenerId
@@ -472,8 +456,7 @@
public void onServiceUnregistered(NsdServiceInfo serviceInfo) {
checkOnHandlerThread();
for (INsdStatusReceiver receiver : mUnregistrationReceivers) {
- Log.i(
- TAG,
+ LOG.i(
"Unregistered successfully. "
+ "Listener ID: "
+ mListenerId
@@ -505,8 +488,7 @@
@Override
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
- Log.e(
- TAG,
+ LOG.e(
"Failed to start service discovery."
+ " Error code: "
+ errorCode
@@ -517,8 +499,7 @@
@Override
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
- Log.e(
- TAG,
+ LOG.e(
"Failed to stop service discovery."
+ " Error code: "
+ errorCode
@@ -529,18 +510,18 @@
@Override
public void onDiscoveryStarted(String serviceType) {
- Log.i(TAG, "Started service discovery. Listener: " + this);
+ LOG.i("Started service discovery. Listener: " + this);
}
@Override
public void onDiscoveryStopped(String serviceType) {
- Log.i(TAG, "Stopped service discovery. Listener: " + this);
+ LOG.i("Stopped service discovery. Listener: " + this);
mDiscoveryListeners.remove(mListenerId);
}
@Override
public void onServiceFound(NsdServiceInfo serviceInfo) {
- Log.i(TAG, "Found service: " + serviceInfo);
+ LOG.i("Found service: " + serviceInfo);
try {
mDiscoverServiceCallback.onServiceDiscovered(
serviceInfo.getServiceName(), mType, true);
@@ -551,7 +532,7 @@
@Override
public void onServiceLost(NsdServiceInfo serviceInfo) {
- Log.i(TAG, "Lost service: " + serviceInfo);
+ LOG.i("Lost service: " + serviceInfo);
try {
mDiscoverServiceCallback.onServiceDiscovered(
serviceInfo.getServiceName(), mType, false);
@@ -584,8 +565,7 @@
@Override
public void onServiceInfoCallbackRegistrationFailed(int errorCode) {
- Log.e(
- TAG,
+ LOG.e(
"Failed to register service info callback."
+ " Listener ID: "
+ mListenerId
@@ -599,8 +579,7 @@
@Override
public void onServiceUpdated(@NonNull NsdServiceInfo serviceInfo) {
- Log.i(
- TAG,
+ LOG.i(
"Service is resolved. "
+ " Listener ID: "
+ mListenerId
@@ -640,7 +619,7 @@
@Override
public void onServiceInfoCallbackUnregistered() {
- Log.i(TAG, "The service info callback is unregistered. Listener: " + this);
+ LOG.i("The service info callback is unregistered. Listener: " + this);
mServiceInfoListeners.remove(mListenerId);
}
@@ -671,8 +650,7 @@
public void onAnswer(@NonNull List<InetAddress> answerList, int rcode) {
checkOnHandlerThread();
- Log.i(
- TAG,
+ LOG.i(
"Host is resolved."
+ " Listener ID: "
+ mListenerId
@@ -698,14 +676,14 @@
public void onError(@NonNull DnsResolver.DnsException error) {
checkOnHandlerThread();
- Log.i(
- TAG,
+ LOG.i(
"Failed to resolve host."
+ " Listener ID: "
+ mListenerId
+ ", hostname: "
- + mHostname,
- error);
+ + mHostname
+ + ", error: "
+ + error.getMessage());
try {
mResolveHostCallback.onHostResolved(mHostname, Collections.emptyList());
} catch (RemoteException e) {
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
index 93b2f70..6edaae9 100644
--- a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
@@ -112,11 +112,11 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserManager;
-import android.util.Log;
import android.util.SparseArray;
import com.android.connectivity.resources.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.SharedLog;
import com.android.server.ServiceManagerWrapper;
import com.android.server.connectivity.ConnectivityResources;
import com.android.server.thread.openthread.BackboneRouterState;
@@ -160,7 +160,8 @@
*/
@TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
final class ThreadNetworkControllerService extends IThreadNetworkController.Stub {
- private static final String TAG = "ThreadNetworkService";
+ private static final String TAG = "ControllerService";
+ private static final SharedLog LOG = ThreadNetworkLogger.forSubComponent(TAG);
// The model name length in utf-8 bytes
private static final int MAX_MODEL_NAME_UTF8_BYTES = 24;
@@ -303,12 +304,12 @@
return;
}
- Log.i(TAG, "Starting OT daemon...");
+ LOG.i("Starting OT daemon...");
try {
getOtDaemon();
} catch (RemoteException e) {
- Log.e(TAG, "Failed to initialize ot-daemon", e);
+ LOG.e("Failed to initialize ot-daemon", e);
} catch (ThreadNetworkException e) {
// no ThreadNetworkException.ERROR_THREAD_DISABLED error should be thrown
throw new AssertionError(e);
@@ -422,7 +423,7 @@
private void onOtDaemonDied() {
checkOnHandlerThread();
- Log.w(TAG, "OT daemon is dead, clean up...");
+ LOG.w("OT daemon is dead, clean up...");
OperationReceiverWrapper.onOtDaemonDied();
mOtDaemonCallbackProxy.onOtDaemonDied();
@@ -435,8 +436,7 @@
public void initialize() {
mHandler.post(
() -> {
- Log.d(
- TAG,
+ LOG.v(
"Initializing Thread system service: Thread is "
+ (shouldEnableThread() ? "enabled" : "disabled"));
try {
@@ -493,7 +493,7 @@
// become dead, so that it's guaranteed that ot-daemon is stopped when {@code
// receiver} is completed
} catch (RemoteException e) {
- Log.e(TAG, "otDaemon.terminate failed", e);
+ LOG.e("otDaemon.terminate failed", e);
receiver.onError(ERROR_INTERNAL_ERROR, "Thread stack error");
} catch (ThreadNetworkException e) {
// No ThreadNetworkException.ERROR_THREAD_DISABLED error will be thrown
@@ -524,7 +524,7 @@
return;
}
- Log.i(TAG, "Set Thread enabled: " + isEnabled + ", persist: " + persist);
+ LOG.i("Set Thread enabled: " + isEnabled + ", persist: " + persist);
if (persist) {
// The persistent setting keeps the desired enabled state, thus it's set regardless
@@ -536,7 +536,7 @@
try {
getOtDaemon().setThreadEnabled(isEnabled, newOtStatusReceiver(receiver));
} catch (RemoteException | ThreadNetworkException e) {
- Log.e(TAG, "otDaemon.setThreadEnabled failed", e);
+ LOG.e("otDaemon.setThreadEnabled failed", e);
receiver.onError(e);
}
}
@@ -553,7 +553,7 @@
@NonNull IOperationReceiver operationReceiver) {
checkOnHandlerThread();
- Log.i(TAG, "Set Thread configuration: " + configuration);
+ LOG.i("Set Thread configuration: " + configuration);
final boolean changed = mPersistentSettings.putConfiguration(configuration);
try {
@@ -631,8 +631,7 @@
if (mUserRestricted == newUserRestrictedState) {
return;
}
- Log.i(
- TAG,
+ LOG.i(
"Thread user restriction changed: "
+ mUserRestricted
+ " -> "
@@ -644,16 +643,14 @@
new IOperationReceiver.Stub() {
@Override
public void onSuccess() {
- Log.d(
- TAG,
+ LOG.v(
(shouldEnableThread ? "Enabled" : "Disabled")
+ " Thread due to user restriction change");
}
@Override
public void onError(int errorCode, String errorMessage) {
- Log.e(
- TAG,
+ LOG.e(
"Failed to "
+ (shouldEnableThread ? "enable" : "disable")
+ " Thread for user restriction change");
@@ -702,13 +699,13 @@
@Override
public void onAvailable(@NonNull Network network) {
checkOnHandlerThread();
- Log.i(TAG, "Upstream network available: " + network);
+ LOG.i("Upstream network available: " + network);
}
@Override
public void onLost(@NonNull Network network) {
checkOnHandlerThread();
- Log.i(TAG, "Upstream network lost: " + network);
+ LOG.i("Upstream network lost: " + network);
// TODO: disable border routing when upsteam network disconnected
}
@@ -723,7 +720,7 @@
if (Objects.equals(existingIfName, newIfName)) {
return;
}
- Log.i(TAG, "Upstream network changed: " + existingIfName + " -> " + newIfName);
+ LOG.i("Upstream network changed: " + existingIfName + " -> " + newIfName);
mNetworkToInterface.put(network, newIfName);
// TODO: disable border routing if netIfName is null
@@ -737,13 +734,13 @@
@Override
public void onAvailable(@NonNull Network network) {
checkOnHandlerThread();
- Log.i(TAG, "Thread network is available: " + network);
+ LOG.i("Thread network is available: " + network);
}
@Override
public void onLost(@NonNull Network network) {
checkOnHandlerThread();
- Log.i(TAG, "Thread network is lost: " + network);
+ LOG.i("Thread network is lost: " + network);
disableBorderRouting();
}
@@ -751,8 +748,7 @@
public void onLocalNetworkInfoChanged(
@NonNull Network network, @NonNull LocalNetworkInfo localNetworkInfo) {
checkOnHandlerThread();
- Log.i(
- TAG,
+ LOG.i(
"LocalNetworkInfo of Thread network changed: {threadNetwork: "
+ network
+ ", localNetworkInfo: "
@@ -810,7 +806,7 @@
return new NetworkAgent(
mContext,
mHandler.getLooper(),
- TAG,
+ LOG.getTag(),
netCaps,
mTunIfController.getLinkProperties(),
newLocalNetworkConfig(),
@@ -827,7 +823,7 @@
mNetworkAgent = newNetworkAgent();
mNetworkAgent.register();
mNetworkAgent.markConnected();
- Log.i(TAG, "Registered Thread network");
+ LOG.i("Registered Thread network");
}
private void unregisterThreadNetwork() {
@@ -837,7 +833,7 @@
return;
}
- Log.d(TAG, "Unregistering Thread network agent");
+ LOG.v("Unregistering Thread network agent");
mNetworkAgent.unregister();
mNetworkAgent = null;
@@ -863,7 +859,7 @@
try {
getOtDaemon().getChannelMasks(newChannelMasksReceiver(networkName, receiver));
} catch (RemoteException | ThreadNetworkException e) {
- Log.e(TAG, "otDaemon.getChannelMasks failed", e);
+ LOG.e("otDaemon.getChannelMasks failed", e);
receiver.onError(e);
}
}
@@ -904,7 +900,7 @@
now = clock.instant();
authoritative = true;
} catch (DateTimeException e) {
- Log.w(TAG, "Failed to get authoritative time", e);
+ LOG.w("Failed to get authoritative time: " + e.getMessage());
}
int panId = random.nextInt(/* bound= */ 0xffff);
@@ -1095,7 +1091,7 @@
// The otDaemon.join() will leave first if this device is currently attached
getOtDaemon().join(activeDataset.toThreadTlvs(), newOtStatusReceiver(receiver));
} catch (RemoteException | ThreadNetworkException e) {
- Log.e(TAG, "otDaemon.join failed", e);
+ LOG.e("otDaemon.join failed", e);
receiver.onError(e);
}
}
@@ -1120,7 +1116,7 @@
.scheduleMigration(
pendingDataset.toThreadTlvs(), newOtStatusReceiver(receiver));
} catch (RemoteException | ThreadNetworkException e) {
- Log.e(TAG, "otDaemon.scheduleMigration failed", e);
+ LOG.e("otDaemon.scheduleMigration failed", e);
receiver.onError(e);
}
}
@@ -1138,7 +1134,7 @@
try {
getOtDaemon().leave(newOtStatusReceiver(receiver));
} catch (RemoteException | ThreadNetworkException e) {
- Log.e(TAG, "otDaemon.leave failed", e);
+ LOG.e("otDaemon.leave failed", e);
receiver.onError(e);
}
}
@@ -1171,7 +1167,7 @@
try {
getOtDaemon().setCountryCode(countryCode, newOtStatusReceiver(receiver));
} catch (RemoteException | ThreadNetworkException e) {
- Log.e(TAG, "otDaemon.setCountryCode failed", e);
+ LOG.e("otDaemon.setCountryCode failed", e);
receiver.onError(e);
}
}
@@ -1181,7 +1177,7 @@
@Nullable String testNetworkInterfaceName, @NonNull IOperationReceiver receiver) {
enforceAllPermissionsGranted(PERMISSION_THREAD_NETWORK_PRIVILEGED, NETWORK_SETTINGS);
- Log.i(TAG, "setTestNetworkAsUpstream: " + testNetworkInterfaceName);
+ LOG.i("setTestNetworkAsUpstream: " + testNetworkInterfaceName);
mHandler.post(() -> setTestNetworkAsUpstreamInternal(testNetworkInterfaceName, receiver));
}
@@ -1227,7 +1223,7 @@
try {
getOtDaemon().setChannelMaxPowers(channelMaxPowers, newOtStatusReceiver(receiver));
} catch (RemoteException | ThreadNetworkException e) {
- Log.e(TAG, "otDaemon.setChannelMaxPowers failed", e);
+ LOG.e("otDaemon.setChannelMaxPowers failed", e);
receiver.onError(ERROR_INTERNAL_ERROR, "Thread stack error");
}
}
@@ -1236,7 +1232,7 @@
if (mInfraLinkState.equals(infraLinkState)) {
return;
}
- Log.i(TAG, "Infra link state changed: " + mInfraLinkState + " -> " + infraLinkState);
+ LOG.i("Infra link state changed: " + mInfraLinkState + " -> " + infraLinkState);
mInfraLinkState = infraLinkState;
ParcelFileDescriptor infraIcmp6Socket = null;
if (mInfraLinkState.interfaceName != null) {
@@ -1244,7 +1240,7 @@
infraIcmp6Socket =
mInfraIfController.createIcmp6Socket(mInfraLinkState.interfaceName);
} catch (IOException e) {
- Log.i(TAG, "Failed to create ICMPv6 socket on infra network interface", e);
+ LOG.e("Failed to create ICMPv6 socket on infra network interface", e);
}
}
try {
@@ -1252,16 +1248,16 @@
.setInfraLinkState(
mInfraLinkState,
infraIcmp6Socket,
- new setInfraLinkStateStatusReceiver());
+ new LoggingOtStatusReceiver("setInfraLinkState"));
} catch (RemoteException | ThreadNetworkException e) {
- Log.w(TAG, "Failed to configure border router " + mOtDaemonConfig, e);
+ LOG.e("Failed to configure border router " + mOtDaemonConfig, e);
}
}
private void enableBorderRouting(String infraIfName) {
InfraLinkState infraLinkState =
newInfraLinkStateBuilder(mInfraLinkState).setInterfaceName(infraIfName).build();
- Log.i(TAG, "Enable border routing on AIL: " + infraIfName);
+ LOG.i("Enable border routing on AIL: " + infraIfName);
setInfraLinkState(infraLinkState);
}
@@ -1269,28 +1265,28 @@
mUpstreamNetwork = null;
InfraLinkState infraLinkState =
newInfraLinkStateBuilder(mInfraLinkState).setInterfaceName(null).build();
- Log.i(TAG, "Disabling border routing");
+ LOG.i("Disabling border routing");
setInfraLinkState(infraLinkState);
}
private void handleThreadInterfaceStateChanged(boolean isUp) {
try {
mTunIfController.setInterfaceUp(isUp);
- Log.i(TAG, "Thread TUN interface becomes " + (isUp ? "up" : "down"));
+ LOG.i("Thread TUN interface becomes " + (isUp ? "up" : "down"));
} catch (IOException e) {
- Log.e(TAG, "Failed to handle Thread interface state changes", e);
+ LOG.e("Failed to handle Thread interface state changes", e);
}
}
private void handleDeviceRoleChanged(@DeviceRole int deviceRole) {
if (ThreadNetworkController.isAttached(deviceRole)) {
- Log.i(TAG, "Attached to the Thread network");
+ LOG.i("Attached to the Thread network");
// This is an idempotent method which can be called for multiple times when the device
// is already attached (e.g. going from Child to Router)
registerThreadNetwork();
} else {
- Log.i(TAG, "Detached from the Thread network");
+ LOG.i("Detached from the Thread network");
// This is an idempotent method which can be called for multiple times when the device
// is already detached or stopped
@@ -1328,7 +1324,7 @@
}
final LocalNetworkConfig localNetworkConfig = newLocalNetworkConfig();
mNetworkAgent.sendLocalNetworkConfig(localNetworkConfig);
- Log.d(TAG, "Sent localNetworkConfig: " + localNetworkConfig);
+ LOG.v("Sent localNetworkConfig: " + localNetworkConfig);
}
private void handleMulticastForwardingChanged(BackboneRouterState state) {
@@ -1401,32 +1397,21 @@
}
}
- private static final class setOtDaemonConfigurationStatusReceiver
- extends IOtStatusReceiver.Stub {
- public setOtDaemonConfigurationStatusReceiver() {}
+ private static class LoggingOtStatusReceiver extends IOtStatusReceiver.Stub {
+ private final String mAction;
+
+ LoggingOtStatusReceiver(String action) {
+ mAction = action;
+ }
@Override
public void onSuccess() {
- Log.i(TAG, "Configured border router successfully");
+ LOG.i("The action " + mAction + " succeeded");
}
@Override
public void onError(int i, String s) {
- Log.w(TAG, String.format("Failed to set configurations: %d %s", i, s));
- }
- }
-
- private static final class setInfraLinkStateStatusReceiver extends IOtStatusReceiver.Stub {
- public setInfraLinkStateStatusReceiver() {}
-
- @Override
- public void onSuccess() {
- Log.i(TAG, "Set the infra link state successfully");
- }
-
- @Override
- public void onError(int i, String s) {
- Log.w(TAG, String.format("Failed to set the infra link state: %d %s", i, s));
+ LOG.w("The action " + mAction + " failed: " + i + " " + s);
}
}
@@ -1463,7 +1448,7 @@
try {
getOtDaemon().registerStateCallback(this, callbackMetadata.id);
} catch (RemoteException | ThreadNetworkException e) {
- Log.e(TAG, "otDaemon.registerStateCallback failed", e);
+ LOG.e("otDaemon.registerStateCallback failed", e);
}
}
@@ -1495,7 +1480,7 @@
try {
getOtDaemon().registerStateCallback(this, callbackMetadata.id);
} catch (RemoteException | ThreadNetworkException e) {
- Log.e(TAG, "otDaemon.registerStateCallback failed", e);
+ LOG.e("otDaemon.registerStateCallback failed", e);
}
}
@@ -1587,7 +1572,7 @@
mActiveDataset = newActiveDataset;
} catch (IllegalArgumentException e) {
// Is unlikely that OT will generate invalid Operational Dataset
- Log.wtf(TAG, "Invalid Active Operational Dataset from OpenThread", e);
+ LOG.wtf("Invalid Active Operational Dataset from OpenThread", e);
}
PendingOperationalDataset newPendingDataset;
@@ -1602,7 +1587,7 @@
mPendingDataset = newPendingDataset;
} catch (IllegalArgumentException e) {
// Is unlikely that OT will generate invalid Operational Dataset
- Log.wtf(TAG, "Invalid Pending Operational Dataset from OpenThread", e);
+ LOG.wtf("Invalid Pending Operational Dataset from OpenThread", e);
}
}
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkCountryCode.java b/thread/service/java/com/android/server/thread/ThreadNetworkCountryCode.java
index a194114..2cd34e8 100644
--- a/thread/service/java/com/android/server/thread/ThreadNetworkCountryCode.java
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkCountryCode.java
@@ -38,10 +38,10 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
-import android.util.Log;
import com.android.connectivity.resources.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.ConnectivityResources;
import java.io.FileDescriptor;
@@ -63,7 +63,9 @@
*/
@TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
public class ThreadNetworkCountryCode {
- private static final String TAG = "ThreadNetworkCountryCode";
+ private static final String TAG = "CountryCode";
+ private static final SharedLog LOG = ThreadNetworkLogger.forSubComponent(TAG);
+
// To be used when there is no country code available.
@VisibleForTesting public static final String DEFAULT_COUNTRY_CODE = "WW";
@@ -280,11 +282,11 @@
String countryCode = addresses.get(0).getCountryCode();
if (isValidCountryCode(countryCode)) {
- Log.d(TAG, "Set location country code to: " + countryCode);
+ LOG.v("Set location country code to: " + countryCode);
mLocationCountryCodeInfo =
new CountryCodeInfo(countryCode, COUNTRY_CODE_SOURCE_LOCATION);
} else {
- Log.d(TAG, "Received invalid location country code");
+ LOG.v("Received invalid location country code");
mLocationCountryCodeInfo = null;
}
@@ -296,8 +298,7 @@
if ((location == null) || (mGeocoder == null)) return;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.TIRAMISU) {
- Log.wtf(
- TAG,
+ LOG.wtf(
"Unexpected call to set country code from the Geocoding location, "
+ "Thread code never runs under T or lower.");
return;
@@ -320,13 +321,13 @@
private class WifiCountryCodeCallback implements ActiveCountryCodeChangedCallback {
@Override
public void onActiveCountryCodeChanged(String countryCode) {
- Log.d(TAG, "Wifi country code is changed to " + countryCode);
+ LOG.v("Wifi country code is changed to " + countryCode);
synchronized ("ThreadNetworkCountryCode.this") {
if (isValidCountryCode(countryCode)) {
mWifiCountryCodeInfo =
new CountryCodeInfo(countryCode, COUNTRY_CODE_SOURCE_WIFI);
} else {
- Log.w(TAG, "WiFi country code " + countryCode + " is invalid");
+ LOG.w("WiFi country code " + countryCode + " is invalid");
mWifiCountryCodeInfo = null;
}
@@ -336,7 +337,7 @@
@Override
public void onCountryCodeInactive() {
- Log.d(TAG, "Wifi country code is inactived");
+ LOG.v("Wifi country code is inactived");
synchronized ("ThreadNetworkCountryCode.this") {
mWifiCountryCodeInfo = null;
updateCountryCode(false /* forceUpdate */);
@@ -346,8 +347,7 @@
private synchronized void registerTelephonyCountryCodeCallback() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
- Log.wtf(
- TAG,
+ LOG.wtf(
"Unexpected call to register the telephony country code changed callback, "
+ "Thread code never runs under T or lower.");
return;
@@ -387,7 +387,7 @@
mSubscriptionManager.getActiveSubscriptionInfoList();
if (subscriptionInfoList == null) {
- Log.d(TAG, "No SIM card is found");
+ LOG.v("No SIM card is found");
return;
}
@@ -399,11 +399,11 @@
try {
countryCode = mTelephonyManager.getNetworkCountryIso(slotIndex);
} catch (IllegalArgumentException e) {
- Log.e(TAG, "Failed to get country code for slot index:" + slotIndex, e);
+ LOG.e("Failed to get country code for slot index:" + slotIndex, e);
continue;
}
- Log.d(TAG, "Telephony slot " + slotIndex + " country code is " + countryCode);
+ LOG.v("Telephony slot " + slotIndex + " country code is " + countryCode);
setTelephonyCountryCodeAndLastKnownCountryCode(
slotIndex, countryCode, null /* lastKnownCountryCode */);
}
@@ -411,8 +411,7 @@
private synchronized void setTelephonyCountryCodeAndLastKnownCountryCode(
int slotIndex, String countryCode, String lastKnownCountryCode) {
- Log.d(
- TAG,
+ LOG.v(
"Set telephony country code to: "
+ countryCode
+ ", last country code to: "
@@ -522,8 +521,7 @@
@Override
public void onError(int otError, String message) {
- Log.e(
- TAG,
+ LOG.e(
"Error "
+ otError
+ ": "
@@ -545,11 +543,11 @@
CountryCodeInfo countryCodeInfo = pickCountryCode();
if (!forceUpdate && countryCodeInfo.isCountryCodeMatch(mCurrentCountryCodeInfo)) {
- Log.i(TAG, "Ignoring already set country code " + countryCodeInfo.getCountryCode());
+ LOG.i("Ignoring already set country code " + countryCodeInfo.getCountryCode());
return;
}
- Log.i(TAG, "Set country code: " + countryCodeInfo);
+ LOG.i("Set country code: " + countryCodeInfo);
mThreadNetworkControllerService.setCountryCode(
countryCodeInfo.getCountryCode().toUpperCase(Locale.ROOT),
newOperationReceiver(countryCodeInfo));
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkLogger.java b/thread/service/java/com/android/server/thread/ThreadNetworkLogger.java
new file mode 100644
index 0000000..a765304
--- /dev/null
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkLogger.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.thread;
+
+import com.android.net.module.util.SharedLog;
+
+/**
+ * The Logger for Thread network.
+ *
+ * <p>Each class should log with its own tag using the logger of
+ * ThreadNetworkLogger.forSubComponent(TAG).
+ */
+public final class ThreadNetworkLogger {
+ private static final String TAG = "ThreadNetwork";
+ private static final SharedLog mLog = new SharedLog(TAG);
+
+ public static SharedLog forSubComponent(String subComponent) {
+ return mLog.forSubComponent(subComponent);
+ }
+
+ // Disable instantiation
+ private ThreadNetworkLogger() {}
+}
diff --git a/thread/service/java/com/android/server/thread/ThreadPersistentSettings.java b/thread/service/java/com/android/server/thread/ThreadPersistentSettings.java
index 7c4c72d..fc18ef9 100644
--- a/thread/service/java/com/android/server/thread/ThreadPersistentSettings.java
+++ b/thread/service/java/com/android/server/thread/ThreadPersistentSettings.java
@@ -25,11 +25,11 @@
import android.net.thread.ThreadConfiguration;
import android.os.PersistableBundle;
import android.util.AtomicFile;
-import android.util.Log;
import com.android.connectivity.resources.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.SharedLog;
import com.android.server.connectivity.ConnectivityResources;
import java.io.ByteArrayInputStream;
@@ -48,6 +48,7 @@
*/
public class ThreadPersistentSettings {
private static final String TAG = "ThreadPersistentSettings";
+ private static final SharedLog LOG = ThreadNetworkLogger.forSubComponent(TAG);
/** File name used for storing settings. */
private static final String FILE_NAME = "ThreadPersistentSettings.xml";
@@ -115,7 +116,7 @@
readFromStoreFile();
synchronized (mLock) {
if (!mSettings.containsKey(THREAD_ENABLED.key)) {
- Log.i(TAG, "\"thread_enabled\" is missing in settings file, using default value");
+ LOG.i("\"thread_enabled\" is missing in settings file, using default value");
put(
THREAD_ENABLED.key,
mResources.get().getBoolean(R.bool.config_thread_default_enabled));
@@ -243,7 +244,7 @@
writeToAtomicFile(mAtomicFile, outputStream.toByteArray());
}
} catch (IOException e) {
- Log.wtf(TAG, "Write to store file failed", e);
+ LOG.wtf("Write to store file failed", e);
}
}
@@ -251,7 +252,7 @@
try {
final byte[] readData;
synchronized (mLock) {
- Log.i(TAG, "Reading from store file: " + mAtomicFile.getBaseFile());
+ LOG.i("Reading from store file: " + mAtomicFile.getBaseFile());
readData = readFromAtomicFile(mAtomicFile);
}
final ByteArrayInputStream inputStream = new ByteArrayInputStream(readData);
@@ -262,9 +263,9 @@
mSettings.putAll(bundleRead);
}
} catch (FileNotFoundException e) {
- Log.w(TAG, "No store file to read", e);
+ LOG.w("No store file to read " + e.getMessage());
} catch (IOException e) {
- Log.e(TAG, "Read from store file failed", e);
+ LOG.e("Read from store file failed", e);
}
}
diff --git a/thread/service/java/com/android/server/thread/TunInterfaceController.java b/thread/service/java/com/android/server/thread/TunInterfaceController.java
index 976f93d..85a0371 100644
--- a/thread/service/java/com/android/server/thread/TunInterfaceController.java
+++ b/thread/service/java/com/android/server/thread/TunInterfaceController.java
@@ -38,10 +38,10 @@
import android.os.SystemClock;
import android.system.ErrnoException;
import android.system.Os;
-import android.util.Log;
import com.android.net.module.util.HexDump;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
+import com.android.net.module.util.SharedLog;
import com.android.net.module.util.netlink.NetlinkUtils;
import com.android.net.module.util.netlink.StructIfinfoMsg;
import com.android.net.module.util.netlink.StructNlAttr;
@@ -66,6 +66,7 @@
public class TunInterfaceController {
private static final String TAG = "TunIfController";
private static final boolean DBG = false;
+ private static final SharedLog LOG = ThreadNetworkLogger.forSubComponent(TAG);
private static final long INFINITE_LIFETIME = 0xffffffffL;
static final int MTU = 1280;
@@ -147,7 +148,7 @@
/** Adds a new address to the interface. */
public void addAddress(LinkAddress address) {
- Log.d(TAG, "Adding address " + address + " with flags: " + address.getFlags());
+ LOG.v("Adding address " + address + " with flags: " + address.getFlags());
long preferredLifetimeSeconds;
long validLifetimeSeconds;
@@ -180,7 +181,7 @@
(byte) address.getScope(),
preferredLifetimeSeconds,
validLifetimeSeconds)) {
- Log.w(TAG, "Failed to add address " + address.getAddress().getHostAddress());
+ LOG.w("Failed to add address " + address.getAddress().getHostAddress());
return;
}
mLinkProperties.addLinkAddress(address);
@@ -189,7 +190,7 @@
/** Removes an address from the interface. */
public void removeAddress(LinkAddress address) {
- Log.d(TAG, "Removing address " + address);
+ LOG.v("Removing address " + address);
// Intentionally update the mLinkProperties before send netlink message because the
// address is already removed from ot-daemon and apps can't reach to the address even
@@ -200,7 +201,7 @@
Os.if_nametoindex(mIfName),
(Inet6Address) address.getAddress(),
(short) address.getPrefixLength())) {
- Log.w(TAG, "Failed to remove address " + address.getAddress().getHostAddress());
+ LOG.w("Failed to remove address " + address.getAddress().getHostAddress());
}
}
@@ -287,7 +288,7 @@
try {
setInterfaceUp(false);
} catch (IOException e) {
- Log.e(TAG, "Failed to set Thread TUN interface down");
+ LOG.e("Failed to set Thread TUN interface down");
}
}
@@ -347,11 +348,15 @@
if (e.getCause() instanceof ErrnoException) {
ErrnoException ee = (ErrnoException) e.getCause();
if (ee.errno == EADDRINUSE) {
- Log.w(TAG, "Already joined group" + address.getHostAddress(), e);
+ LOG.w(
+ "Already joined group "
+ + address.getHostAddress()
+ + ": "
+ + e.getMessage());
return;
}
}
- Log.e(TAG, "failed to join group " + address.getHostAddress(), e);
+ LOG.e("failed to join group " + address.getHostAddress(), e);
}
}
@@ -360,7 +365,7 @@
try {
mMulticastSocket.leaveGroup(socketAddress, mNetworkInterface);
} catch (IOException e) {
- Log.e(TAG, "failed to leave group " + address.getHostAddress(), e);
+ LOG.e("failed to leave group " + address.getHostAddress(), e);
}
}
@@ -415,14 +420,14 @@
}
if (DBG) {
- Log.d(TAG, "ADDR_GEN_MODE message is:");
- Log.d(TAG, HexDump.dumpHexString(msg));
+ LOG.v("ADDR_GEN_MODE message is:");
+ LOG.v(HexDump.dumpHexString(msg));
}
try {
NetlinkUtils.sendOneShotKernelMessage(NETLINK_ROUTE, msg);
} catch (ErrnoException e) {
- Log.e(TAG, "Failed to set ADDR_GEN_MODE to NONE", e);
+ LOG.e("Failed to set ADDR_GEN_MODE to NONE", e);
}
}
}
diff --git a/thread/tests/cts/AndroidTest.xml b/thread/tests/cts/AndroidTest.xml
index 6eda1e9..34aabe2 100644
--- a/thread/tests/cts/AndroidTest.xml
+++ b/thread/tests/cts/AndroidTest.xml
@@ -22,6 +22,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user_on_secondary_display" />
<!--
Only run tests if the device under test is SDK version 33 (Android 13) or above.