Merge changes from topic "revert-3548747-TOVIIQGWFJ" into main
* changes:
Revert "Make iterating over mNetworkAgentInfos safer."
Revert "Always disconnect agents immediately."
Revert "Immediately create networks unless chickened out"
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
index 3c91a1b..5f0e5d0 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java
@@ -24,6 +24,7 @@
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
import static com.android.networkstack.apishim.ConstantsShim.KEY_CARRIER_SUPPORTS_TETHERING_BOOL;
+import static com.android.net.module.util.SdkUtil.isAtLeast25Q2;
import android.content.ContentResolver;
import android.content.Context;
@@ -395,7 +396,7 @@
* use the async state machine.
*/
public void readEnableSyncSM(final Context ctx) {
- USE_SYNC_SM = mDeps.isFeatureNotChickenedOut(ctx, TETHER_ENABLE_SYNC_SM);
+ USE_SYNC_SM = isAtLeast25Q2() || mDeps.isFeatureNotChickenedOut(ctx, TETHER_ENABLE_SYNC_SM);
}
/** Does the dumping.*/
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
index f501a50..b92cf69 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
@@ -22,6 +22,7 @@
import static android.Manifest.permission.TETHER_PRIVILEGED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
+import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION;
import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
@@ -58,6 +59,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.networkstack.apishim.SettingsShimImpl;
import com.android.networkstack.apishim.common.SettingsShim;
+import com.android.networkstack.tethering.util.TetheringPermissionsUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -72,6 +74,7 @@
private TetheringConnector mConnector;
private SettingsShim mSettingsShim;
+ private TetheringPermissionsUtils mTetheringPermissionsUtils;
@Override
public void onCreate() {
@@ -81,6 +84,7 @@
mConnector = new TetheringConnector(makeTethering(deps), TetheringService.this);
mSettingsShim = SettingsShimImpl.newInstance();
+ mTetheringPermissionsUtils = new TetheringPermissionsUtils(deps.getContext());
}
/**
@@ -109,7 +113,11 @@
@Override
public void tether(String iface, String callerPkg, String callingAttributionTag,
IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
+ if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
+ false /* onlyAllowPrivileged */, false /* isDeviceOwnerAppAllowed */,
+ listener)) {
+ return;
+ }
mTethering.legacyTether(iface, listener);
}
@@ -117,7 +125,11 @@
@Override
public void untether(String iface, String callerPkg, String callingAttributionTag,
IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
+ if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
+ false /* onlyAllowPrivileged */, false /* isDeviceOwnerAppAllowed */,
+ listener)) {
+ return;
+ }
mTethering.legacyUntether(iface, listener);
}
@@ -125,32 +137,45 @@
@Override
public void setUsbTethering(boolean enable, String callerPkg, String callingAttributionTag,
IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
+ if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
+ false /* onlyAllowPrivileged */, false /* isDeviceOwnerAppAllowed */,
+ listener)) {
+ return;
+ }
mTethering.setUsbTethering(enable, listener);
}
+ private boolean isRequestAllowedForDeviceOwner(@NonNull TetheringRequest request) {
+ return request.getTetheringType() == TETHERING_WIFI
+ && request.getSoftApConfiguration() != null;
+ }
+
@Override
- public void startTethering(TetheringRequestParcel request, String callerPkg,
+ public void startTethering(TetheringRequestParcel requestParcel, String callerPkg,
String callingAttributionTag, IIntResultListener listener) {
- boolean onlyAllowPrivileged = request.exemptFromEntitlementCheck
- || request.interfaceName != null;
- if (checkAndNotifyCommonError(callerPkg,
- callingAttributionTag,
- onlyAllowPrivileged,
- listener)) {
+ TetheringRequest request = new TetheringRequest(requestParcel);
+ request.setUid(getBinderCallingUid());
+ request.setPackageName(callerPkg);
+ boolean onlyAllowPrivileged = request.isExemptFromEntitlementCheck()
+ || request.getInterfaceName() != null;
+ boolean isDeviceOwnerAllowed = mTethering.isTetheringWithSoftApConfigEnabled()
+ && isRequestAllowedForDeviceOwner(request);
+ if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, onlyAllowPrivileged,
+ isDeviceOwnerAllowed, listener)) {
return;
}
- TetheringRequest external = new TetheringRequest(request);
- external.setUid(getBinderCallingUid());
- external.setPackageName(callerPkg);
- mTethering.startTethering(external, callerPkg, listener);
+ mTethering.startTethering(request, callerPkg, listener);
}
@Override
public void stopTethering(int type, String callerPkg, String callingAttributionTag,
IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
+ if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
+ false /* onlyAllowPrivileged */, false /* isDeviceOwnerAppAllowed */,
+ listener)) {
+ return;
+ }
try {
mTethering.stopTethering(type);
@@ -164,9 +189,16 @@
IIntResultListener listener) {
if (request == null) return;
if (listener == null) return;
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
request.setUid(getBinderCallingUid());
request.setPackageName(callerPkg);
+ boolean isDeviceOwnerAllowed = mTethering.isTetheringWithSoftApConfigEnabled()
+ && isRequestAllowedForDeviceOwner(request);
+ if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
+ false /* onlyAllowPrivileged */, isDeviceOwnerAllowed, listener)) {
+ return;
+ }
+ // Note: Whether tethering is actually stopped or not will depend on whether the request
+ // matches an active one with the same UID (see RequestTracker#findFuzzyMatchedRequest).
mTethering.stopTetheringRequest(request, listener);
}
@@ -187,7 +219,11 @@
+ " internal-only listener");
}
};
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
+ if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
+ false /* onlyAllowPrivileged */, false /* isDeviceOwnerAppAllowed */,
+ listener)) {
+ return;
+ }
mTethering.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi);
}
@@ -223,7 +259,11 @@
@Override
public void stopAllTethering(String callerPkg, String callingAttributionTag,
IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
+ if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
+ false /* onlyAllowPrivileged */, false /* isDeviceOwnerAppAllowed */,
+ listener)) {
+ return;
+ }
try {
mTethering.stopAllTethering();
@@ -234,8 +274,11 @@
@Override
public void isTetheringSupported(String callerPkg, String callingAttributionTag,
IIntResultListener listener) {
- if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, listener)) return;
-
+ boolean isDeviceOwnerAppAllowed = mTethering.isTetheringWithSoftApConfigEnabled();
+ if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
+ false /* onlyAllowPrivileged */, isDeviceOwnerAppAllowed, listener)) {
+ return;
+ }
try {
listener.onResult(TETHER_ERROR_NO_ERROR);
} catch (RemoteException e) { }
@@ -260,23 +303,17 @@
}
private boolean checkAndNotifyCommonError(final String callerPkg,
- final String callingAttributionTag, final IIntResultListener listener) {
- return checkAndNotifyCommonError(callerPkg, callingAttributionTag,
- false /* onlyAllowPrivileged */, listener);
- }
-
- private boolean checkAndNotifyCommonError(final String callerPkg,
final String callingAttributionTag, final boolean onlyAllowPrivileged,
- final IIntResultListener listener) {
+ final boolean isDeviceOwnerAppAllowed, final IIntResultListener listener) {
try {
- if (!checkPackageNameMatchesUid(getBinderCallingUid(), callerPkg)) {
- Log.e(TAG, "Package name " + callerPkg + " does not match UID "
- + getBinderCallingUid());
+ final int uid = getBinderCallingUid();
+ if (!checkPackageNameMatchesUid(uid, callerPkg)) {
+ Log.e(TAG, "Package name " + callerPkg + " does not match UID " + uid);
listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
return true;
}
- if (!hasTetherChangePermission(callerPkg, callingAttributionTag,
- onlyAllowPrivileged)) {
+ if (!hasTetherChangePermission(uid, callerPkg, callingAttributionTag,
+ onlyAllowPrivileged, isDeviceOwnerAppAllowed)) {
listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
return true;
}
@@ -308,21 +345,25 @@
return mService.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
}
- private boolean hasTetherChangePermission(final String callerPkg,
- final String callingAttributionTag, final boolean onlyAllowPrivileged) {
+ private boolean hasTetherChangePermission(final int uid, final String callerPkg,
+ final String callingAttributionTag, final boolean onlyAllowPrivileged,
+ final boolean isDeviceOwnerAppAllowed) {
if (onlyAllowPrivileged && !hasNetworkStackPermission()
&& !hasNetworkSettingsPermission()) return false;
if (hasTetherPrivilegedPermission()) return true;
+ // Allow DO apps to change tethering even if they don't have TETHER_PRIVILEGED.
+ if (isDeviceOwnerAppAllowed && mService.isDeviceOwner(uid, callerPkg)) {
+ return true;
+ }
+
// After TetheringManager moves to public API, prevent third-party apps from being able
// to change tethering with only WRITE_SETTINGS permission.
if (mTethering.isTetheringWithSoftApConfigEnabled()) return false;
if (mTethering.isTetherProvisioningRequired()) return false;
- int uid = getBinderCallingUid();
-
// If callerPkg's uid is not same as getBinderCallingUid(),
// checkAndNoteWriteSettingsOperation will return false and the operation will be
// denied.
@@ -387,6 +428,14 @@
}
/**
+ * Wrapper for {@link TetheringPermissionsUtils#isDeviceOwner(int, String)}, used for mocks.
+ */
+ @VisibleForTesting
+ boolean isDeviceOwner(final int uid, final String callerPkg) {
+ return mTetheringPermissionsUtils.isDeviceOwner(uid, callerPkg);
+ }
+
+ /**
* An injection method for testing.
*/
@VisibleForTesting
diff --git a/Tethering/src/com/android/networkstack/tethering/util/TetheringPermissionsUtils.java b/Tethering/src/com/android/networkstack/tethering/util/TetheringPermissionsUtils.java
new file mode 100644
index 0000000..944e861
--- /dev/null
+++ b/Tethering/src/com/android/networkstack/tethering/util/TetheringPermissionsUtils.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.networkstack.tethering.util;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Utils class for checking permissions related to Tethering APIs.
+ */
+public class TetheringPermissionsUtils {
+ private static final String TAG = "TetherPermUtils";
+
+ @NonNull private final Context mContext;
+
+ public TetheringPermissionsUtils(@NonNull Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Checks if the package name is a Device Owner.
+ */
+ public boolean isDeviceOwner(final int uid, @NonNull final String packageName) {
+ Context userContext;
+ try {
+ // There is no safe way to invoke this method since tethering package might not be
+ // installed for a certain user on the OEM devices, refer to b/382628161.
+ userContext = mContext.createContextAsUser(UserHandle.getUserHandleForUid(uid),
+ 0 /* flags */);
+ } catch (IllegalStateException e) {
+ // TODO: Add a terrible error metric for this case.
+ Log.e(TAG, "createContextAsUser failed, skipping Device Owner check", e);
+ return false;
+ }
+ DevicePolicyManager devicePolicyManager =
+ retrieveDevicePolicyManagerFromContext(userContext);
+ if (devicePolicyManager == null) return false;
+ return devicePolicyManager.isDeviceOwnerApp(packageName);
+ }
+
+ private DevicePolicyManager retrieveDevicePolicyManagerFromContext(
+ @NonNull final Context context) {
+ DevicePolicyManager devicePolicyManager =
+ context.getSystemService(DevicePolicyManager.class);
+ if (devicePolicyManager == null
+ && context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_DEVICE_ADMIN)) {
+ Log.w(TAG, "Error retrieving DPM service");
+ }
+ return devicePolicyManager;
+ }
+}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
index 7fcc5f1..a8bd221 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
@@ -25,14 +25,18 @@
import android.os.Binder;
import android.os.IBinder;
import android.util.ArrayMap;
+import android.util.ArraySet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import java.util.Set;
+
public class MockTetheringService extends TetheringService {
private final Tethering mTethering = mock(Tethering.class);
private final ArrayMap<String, Integer> mMockedPermissions = new ArrayMap<>();
private final ArrayMap<String, Integer> mMockedPackageUids = new ArrayMap<>();
+ private final Set<String> mMockedDeviceOwnerPackages = new ArraySet<>();
private int mMockCallingUid;
@Override
@@ -74,6 +78,11 @@
return mMockCallingUid;
}
+ @Override
+ boolean isDeviceOwner(final int uid, final String callerPkg) {
+ return mMockedDeviceOwnerPackages.contains(callerPkg);
+ }
+
public Tethering getTethering() {
return mTethering;
}
@@ -118,5 +127,19 @@
public void setCallingUid(int uid) {
mMockCallingUid = uid;
}
+
+ /**
+ * Add a mocked carrier privileges package
+ */
+ public void addDeviceOwnerPackage(final String packageName) {
+ mMockedDeviceOwnerPackages.add(packageName);
+ }
+
+ /**
+ * Remove a mocked carrier privileges package
+ */
+ public void removeDeviceOwnerPackage(final String packageName) {
+ mMockedDeviceOwnerPackages.remove(packageName);
+ }
}
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
index 0159573..c4c7c9c 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java
@@ -63,6 +63,7 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.DeviceConfigUtils;
+import com.android.net.module.util.SdkUtil;
import com.android.net.module.util.SharedLog;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter;
@@ -779,7 +780,10 @@
setTetherEnableSyncSMFlagEnabled(true);
assertEnableSyncSM(true);
- setTetherEnableSyncSMFlagEnabled(false);
- assertEnableSyncSM(false);
+ // Feature is enabled by default after 25Q2 release.
+ if (!SdkUtil.isAtLeast25Q2()) {
+ setTetherEnableSyncSMFlagEnabled(false);
+ assertEnableSyncSM(false);
+ }
}
}
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
index b550ada..87163ef 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
@@ -21,6 +21,7 @@
import static android.Manifest.permission.TETHER_PRIVILEGED;
import static android.Manifest.permission.WRITE_SETTINGS;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
+import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_VIRTUAL;
import static android.net.TetheringManager.TETHERING_WIFI;
import static android.net.TetheringManager.TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION;
@@ -36,6 +37,7 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
@@ -53,6 +55,7 @@
import android.net.TetheringManager;
import android.net.TetheringManager.TetheringRequest;
import android.net.TetheringRequestParcel;
+import android.net.wifi.SoftApConfiguration;
import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.Handler;
@@ -153,43 +156,58 @@
}
private void runAsNoPermission(final TestTetheringCall test) throws Exception {
- runTetheringCall(test, true /* isTetheringAllowed */, false /* isWriteSettingsAllowed */,
- new String[0]);
+ runTetheringCall(test, true /* isTetheringAllowed */,
+ true /* isTetheringWithSoftApConfigEnabled */, new String[0]);
}
private void runAsTetherPrivileged(final TestTetheringCall test) throws Exception {
- runTetheringCall(test, true /* isTetheringAllowed */, false /* isWriteSettingsAllowed */,
- TETHER_PRIVILEGED);
+ runTetheringCall(test, true /* isTetheringAllowed */,
+ true /* isTetheringWithSoftApConfigEnabled */, TETHER_PRIVILEGED);
}
private void runAsAccessNetworkState(final TestTetheringCall test) throws Exception {
- runTetheringCall(test, true /* isTetheringAllowed */, false /* isWriteSettingsAllowed */,
- ACCESS_NETWORK_STATE);
+ runTetheringCall(test, true /* isTetheringAllowed */,
+ true /* isTetheringWithSoftApConfigEnabled */, ACCESS_NETWORK_STATE);
}
private void runAsWriteSettings(final TestTetheringCall test) throws Exception {
- runTetheringCall(test, true /* isTetheringAllowed */, false /* isWriteSettingsAllowed */,
- WRITE_SETTINGS);
+ runTetheringCall(test, true /* isTetheringAllowed */,
+ true /* isTetheringWithSoftApConfigEnabled */, WRITE_SETTINGS);
}
private void runAsWriteSettingsWhenWriteSettingsAllowed(
final TestTetheringCall test) throws Exception {
- runTetheringCall(test, true /* isTetheringAllowed */, true /* isWriteSettingsAllowed */,
- WRITE_SETTINGS);
+ runTetheringCall(test, true /* isTetheringAllowed */,
+ false /* isTetheringWithSoftApConfigEnabled */, WRITE_SETTINGS);
}
private void runAsTetheringDisallowed(final TestTetheringCall test) throws Exception {
- runTetheringCall(test, false /* isTetheringAllowed */, false /* isWriteSettingsAllowed */,
- TETHER_PRIVILEGED);
+ runTetheringCall(test, false /* isTetheringAllowed */,
+ true /* isTetheringWithSoftApConfigEnabled */, TETHER_PRIVILEGED);
}
private void runAsNetworkSettings(final TestTetheringCall test) throws Exception {
- runTetheringCall(test, true /* isTetheringAllowed */, false /* isWriteSettingsAllowed */,
- NETWORK_SETTINGS, TETHER_PRIVILEGED);
+ runTetheringCall(test, true /* isTetheringAllowed */,
+ true /* isTetheringWithSoftApConfigEnabled */, NETWORK_SETTINGS, TETHER_PRIVILEGED);
+ }
+
+ private void runAsDeviceOwner(final TestTetheringCall test) throws Exception {
+ mMockConnector.addDeviceOwnerPackage(TEST_CALLER_PKG);
+ runTetheringCall(test, true /* isTetheringAllowed */,
+ true /* isTetheringWithSoftApConfigEnabled */, new String[0]);
+ mMockConnector.removeDeviceOwnerPackage(TEST_CALLER_PKG);
+ }
+
+ private void runAsDeviceOwnerWhenDeviceOwnerBypassNotEnabled(final TestTetheringCall test)
+ throws Exception {
+ mMockConnector.addDeviceOwnerPackage(TEST_CALLER_PKG);
+ runTetheringCall(test, true /* isTetheringAllowed */,
+ false /* isTetheringWithSoftApConfigEnabled */, new String[0]);
+ mMockConnector.removeDeviceOwnerPackage(TEST_CALLER_PKG);
}
private void runTetheringCall(final TestTetheringCall test, boolean isTetheringAllowed,
- boolean isWriteSettingsAllowed, String... permissions) throws Exception {
+ boolean isTetheringWithSoftApConfigEnabled, String... permissions) throws Exception {
// Allow the test to run even if ACCESS_NETWORK_STATE was granted at the APK level
if (!CollectionUtils.contains(permissions, ACCESS_NETWORK_STATE)) {
mMockConnector.setPermission(ACCESS_NETWORK_STATE, PERMISSION_DENIED);
@@ -200,7 +218,7 @@
when(mTethering.isTetheringSupported()).thenReturn(true);
when(mTethering.isTetheringAllowed()).thenReturn(isTetheringAllowed);
when(mTethering.isTetheringWithSoftApConfigEnabled())
- .thenReturn(!isWriteSettingsAllowed);
+ .thenReturn(isTetheringWithSoftApConfigEnabled);
test.runTetheringCall(new TestTetheringResult());
} finally {
mUiAutomation.dropShellPermissionIdentity();
@@ -226,7 +244,7 @@
runAsNoPermission((result) -> {
mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
@@ -239,14 +257,14 @@
runAsWriteSettings((result) -> {
mTetheringConnector.tether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runTether(result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering();
});
@@ -274,7 +292,7 @@
runAsNoPermission((result) -> {
mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
@@ -287,14 +305,14 @@
runAsWriteSettings((result) -> {
mTetheringConnector.untether(TEST_IFACE_NAME, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runUnTether(result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering();
});
@@ -328,7 +346,7 @@
runAsNoPermission((result) -> {
mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
@@ -341,14 +359,14 @@
runAsWriteSettings((result) -> {
mTetheringConnector.setUsbTethering(true /* enable */, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runSetUsbTethering(result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering();
});
@@ -367,6 +385,7 @@
final TetheringRequestParcel request) throws Exception {
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
verify(mTethering).startTethering(
@@ -381,14 +400,61 @@
runAsNoPermission((result) -> {
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
+ // Not a Wifi request - Fail
+ runAsDeviceOwner((result) -> {
+ final TetheringRequestParcel notWifi = new TetheringRequestParcel();
+ notWifi.tetheringType = TETHERING_USB;
+ mTetheringConnector.startTethering(notWifi, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+ result);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
+ result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ // Request has no SoftApConfiguration - Fail
+ runAsDeviceOwner((result) -> {
+ final TetheringRequestParcel noConfig = new TetheringRequestParcel();
+ noConfig.tetheringType = TETHERING_WIFI;
+ mTetheringConnector.startTethering(noConfig, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+ result);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
+ result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ // Wifi request with SoftApConfiguration - Succeed
+ runAsDeviceOwner((result) -> {
+ final TetheringRequestParcel withConfig = new TetheringRequestParcel();
+ withConfig.tetheringType = TETHERING_WIFI;
+ withConfig.softApConfig = new SoftApConfiguration.Builder().build();
+ mTetheringConnector.startTethering(withConfig, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+ result);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering).isTetheringSupported();
+ verify(mTethering).isTetheringAllowed();
+ verify(mTethering).startTethering(any(), any(), any());
+ result.assertResult(-1); // No result
+ verifyNoMoreInteractionsForTethering();
+ });
+
+ runAsDeviceOwnerWhenDeviceOwnerBypassNotEnabled((result) -> {
+ mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
+ result);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
+ result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ verify(mTethering).isTetherProvisioningRequired();
+ verifyNoMoreInteractionsForTethering();
+ });
+
runAsTetherPrivileged((result) -> {
mTetheringConnector.startTethering(request, TEST_WRONG_PACKAGE,
TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering, never()).startTethering(
eq(new TetheringRequest(request)), eq(TEST_WRONG_PACKAGE), eq(result));
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
@@ -403,14 +469,14 @@
runAsWriteSettings((result) -> {
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runStartTethering(result, request);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering();
});
@@ -418,6 +484,7 @@
runAsTetheringDisallowed((result) -> {
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
result.assertResult(TETHER_ERROR_UNSUPPORTED);
@@ -447,6 +514,7 @@
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verifyNoMoreInteractionsForTethering();
});
}
@@ -459,6 +527,7 @@
mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verifyNoMoreInteractionsForTethering();
}
@@ -495,7 +564,7 @@
runAsNoPermission((result) -> {
mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
@@ -508,14 +577,14 @@
runAsWriteSettings((result) -> {
mTetheringConnector.stopTethering(TETHERING_WIFI, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runStopTethering(result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering();
});
@@ -530,6 +599,87 @@
});
}
+ private void verifyHasPermissionForStopTetheringRequest(TetheringRequest request,
+ final TestTetheringResult result) throws Exception {
+ mTetheringConnector.stopTetheringRequest(request, TEST_CALLER_PKG,
+ TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering).stopTetheringRequest(any(), any());
+ verify(mTethering).isTetheringSupported();
+ verify(mTethering).isTetheringAllowed();
+ reset(mTethering);
+ }
+
+ private void verifyDoesNotHavePermissionForStopTetheringRequest(TetheringRequest request,
+ final TestTetheringResult result) throws Exception {
+ mTetheringConnector.stopTetheringRequest(request, TEST_CALLER_PKG,
+ TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering, never()).stopTetheringRequest(any(), any());
+ result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ reset(mTethering);
+ }
+
+ private void verifyStopTetheringRequestWithTetheringDisallowed(TetheringRequest request,
+ final TestTetheringResult result) throws Exception {
+ mTetheringConnector.stopTetheringRequest(request, TEST_CALLER_PKG,
+ TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering, never()).stopTetheringRequest(any(), any());
+ result.assertResult(TETHER_ERROR_UNSUPPORTED);
+ reset(mTethering);
+ }
+
+ @Test
+ public void testStopTetheringRequest() throws Exception {
+ TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
+
+ runAsNoPermission((result) -> {
+ verifyDoesNotHavePermissionForStopTetheringRequest(request, result);
+ });
+
+ runAsTetherPrivileged((result) -> {
+ verifyHasPermissionForStopTetheringRequest(request, result);
+ });
+
+ runAsWriteSettings((result) -> {
+ verifyDoesNotHavePermissionForStopTetheringRequest(request, result);
+ });
+
+ runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
+ verifyHasPermissionForStopTetheringRequest(request, result);
+ });
+
+ runAsTetheringDisallowed((result) -> {
+ verifyStopTetheringRequestWithTetheringDisallowed(request, result);
+ });
+
+ runAsNetworkSettings((result) -> {
+ verifyHasPermissionForStopTetheringRequest(request, result);
+ });
+
+ // Not wifi -> fail
+ runAsDeviceOwner((result) -> {
+ TetheringRequest notWifi = new TetheringRequest.Builder(TETHERING_USB).build();
+ verifyDoesNotHavePermissionForStopTetheringRequest(notWifi, result);
+ });
+
+ // No config -> fail
+ runAsDeviceOwner((result) -> {
+ TetheringRequest noConfig = new TetheringRequest.Builder(TETHERING_WIFI).build();
+ verifyDoesNotHavePermissionForStopTetheringRequest(noConfig, result);
+ });
+
+ // With config -> success
+ TetheringRequest withConfig = new TetheringRequest.Builder(TETHERING_WIFI)
+ .setSoftApConfiguration(new SoftApConfiguration.Builder().build())
+ .build();
+ runAsDeviceOwner((result) -> {
+ verifyHasPermissionForStopTetheringRequest(withConfig, result);
+ });
+
+ runAsDeviceOwnerWhenDeviceOwnerBypassNotEnabled((result) -> {
+ verifyDoesNotHavePermissionForStopTetheringRequest(withConfig, result);
+ });
+ }
+
private void runRequestLatestTetheringEntitlementResult() throws Exception {
final MyResultReceiver result = new MyResultReceiver(null);
mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result,
@@ -547,7 +697,7 @@
runAsNoPermission((none) -> {
mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result,
true /* showEntitlementUi */, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
@@ -567,14 +717,14 @@
runAsWriteSettings((none) -> {
mTetheringConnector.requestLatestTetheringEntitlementResult(TETHERING_WIFI, result,
true /* showEntitlementUi */, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
runAsWriteSettingsWhenWriteSettingsAllowed((none) -> {
runRequestLatestTetheringEntitlementResult();
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering();
});
@@ -668,7 +818,7 @@
public void testStopAllTethering() throws Exception {
runAsNoPermission((result) -> {
mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
@@ -680,14 +830,14 @@
runAsWriteSettings((result) -> {
mTetheringConnector.stopAllTethering(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runStopAllTethering(result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering();
});
@@ -703,6 +853,7 @@
private void runIsTetheringSupported(final TestTetheringResult result) throws Exception {
mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
result.assertResult(TETHER_ERROR_NO_ERROR);
@@ -713,7 +864,7 @@
runAsNoPermission((result) -> {
mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
@@ -726,14 +877,13 @@
runAsWriteSettings((result) -> {
mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
verifyNoMoreInteractionsForTethering();
});
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
runIsTetheringSupported(result);
- verify(mTethering).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetherProvisioningRequired();
verifyNoMoreInteractionsForTethering();
});
@@ -741,6 +891,7 @@
runAsTetheringDisallowed((result) -> {
mTetheringConnector.isTetheringSupported(TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
result);
+ verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
verify(mTethering).isTetheringSupported();
verify(mTethering).isTetheringAllowed();
result.assertResult(TETHER_ERROR_UNSUPPORTED);
diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringPermissionsUtilsTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringPermissionsUtilsTest.java
new file mode 100644
index 0000000..57c3eca
--- /dev/null
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringPermissionsUtilsTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.networkstack.tethering.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.os.UserHandle;
+import android.telephony.TelephonyManager;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class TetheringPermissionsUtilsTest {
+ private static final int TEST_UID = 12345;
+ private static final String TEST_PACKAGE = "test.package";
+
+ @Mock Context mContext;
+ @Mock Context mUserContext;
+ @Mock DevicePolicyManager mDevicePolicyManager;
+ @Mock TelephonyManager mTelephonyManager;
+
+ TetheringPermissionsUtils mTetheringPermissionsUtils;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.createContextAsUser(UserHandle.getUserHandleForUid(TEST_UID), 0))
+ .thenReturn(mUserContext);
+ when(mUserContext.getSystemService(DevicePolicyManager.class))
+ .thenReturn(mDevicePolicyManager);
+ when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+ mTetheringPermissionsUtils = new TetheringPermissionsUtils(mContext);
+ }
+
+ @Test
+ public void testIsDeviceOwner() {
+ when(mDevicePolicyManager.isDeviceOwnerApp(TEST_PACKAGE)).thenReturn(false);
+ assertThat(mTetheringPermissionsUtils.isDeviceOwner(TEST_UID, TEST_PACKAGE)).isFalse();
+
+ when(mDevicePolicyManager.isDeviceOwnerApp(TEST_PACKAGE)).thenReturn(true);
+ assertThat(mTetheringPermissionsUtils.isDeviceOwner(TEST_UID, TEST_PACKAGE)).isTrue();
+ }
+}
diff --git a/bpf/loader/NetBpfLoad.cpp b/bpf/loader/NetBpfLoad.cpp
index b9ef766..d70a2c8 100644
--- a/bpf/loader/NetBpfLoad.cpp
+++ b/bpf/loader/NetBpfLoad.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "NetBpfLoad"
#include <arpa/inet.h>
+#include <bpf/btf.h>
#include <bpf/libbpf.h>
#include <dirent.h>
#include <elf.h>
@@ -51,6 +52,7 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
@@ -649,10 +651,185 @@
return false;
}
+static int setBtfDatasecSize(ifstream &elfFile, struct btf *btf,
+ struct btf_type *bt) {
+ const char *name = btf__name_by_offset(btf, bt->name_off);
+ if (!name) {
+ ALOGE("Couldn't resolve section name, errno: %d", errno);
+ return -errno;
+ }
+
+ vector<char> data;
+ int ret = readSectionByName(name, elfFile, data);
+ if (ret) {
+ ALOGE("Couldn't read section %s, ret: %d", name, ret);
+ return ret;
+ }
+ bt->size = data.size();
+ return 0;
+}
+
+static int getSymOffsetByName(ifstream &elfFile, const char *name, int *off) {
+ vector<Elf64_Sym> symtab;
+ int ret = readSymTab(elfFile, 1 /* sort */, symtab);
+ if (ret) return ret;
+ for (int i = 0; i < (int)symtab.size(); i++) {
+ string s;
+ ret = getSymName(elfFile, symtab[i].st_name, s);
+ if (ret) continue;
+ if (!strcmp(s.c_str(), name)) {
+ *off = symtab[i].st_value;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+static int setBtfVarOffset(ifstream &elfFile, struct btf *btf,
+ struct btf_type *datasecBt) {
+ int i, vars = btf_vlen(datasecBt);
+ struct btf_var_secinfo *vsi;
+ const char *datasecName = btf__name_by_offset(btf, datasecBt->name_off);
+ if (!datasecName) {
+ ALOGE("Couldn't resolve section name, errno: %d", errno);
+ return -errno;
+ }
+
+ for (i = 0, vsi = btf_var_secinfos(datasecBt); i < vars; i++, vsi++) {
+ const struct btf_type *varBt = btf__type_by_id(btf, vsi->type);
+ if (!varBt || !btf_is_var(varBt)) {
+ ALOGE("Found non VAR kind btf_type, section: %s id: %d", datasecName,
+ vsi->type);
+ return -1;
+ }
+
+ const struct btf_var *var = btf_var(varBt);
+ if (var->linkage == BTF_VAR_STATIC) continue;
+
+ const char *varName = btf__name_by_offset(btf, varBt->name_off);
+ if (!varName) {
+ ALOGE("Failed to resolve var name, section: %s", datasecName);
+ return -1;
+ }
+
+ int off;
+ int ret = getSymOffsetByName(elfFile, varName, &off);
+ if (ret) {
+ ALOGE("No offset found in symbol table, section: %s, var: %s, ret: %d",
+ datasecName, varName, ret);
+ return ret;
+ }
+ vsi->offset = off;
+ }
+ return 0;
+}
+
+static int loadBtf(ifstream &elfFile, struct btf *btf) {
+ int ret;
+ for (unsigned int i = 1; i < btf__type_cnt(btf); ++i) {
+ struct btf_type *bt = (struct btf_type *)btf__type_by_id(btf, i);
+ if (!btf_is_datasec(bt)) continue;
+ ret = setBtfDatasecSize(elfFile, btf, bt);
+ if (ret) return ret;
+ ret = setBtfVarOffset(elfFile, btf, bt);
+ if (ret) return ret;
+ }
+
+ ret = btf__load_into_kernel(btf);
+ if (ret) {
+ if (errno != EINVAL) {
+ ALOGE("btf__load_into_kernel failed, errno: %d", errno);
+ return ret;
+ };
+ // For BTF_KIND_FUNC, newer kernels can read the BTF_INFO_VLEN bits of
+ // struct btf_type to distinguish static vs. global vs. extern
+ // functions, but older kernels enforce that only the BTF_INFO_KIND bits
+ // can be set. Retry with non-BTF_INFO_KIND bits zeroed out to handle
+ // this case.
+ for (unsigned int i = 1; i < btf__type_cnt(btf); ++i) {
+ struct btf_type *bt = (struct btf_type *)btf__type_by_id(btf, i);
+ if (btf_is_func(bt)) {
+ bt->info = (BTF_INFO_KIND(bt->info)) << 24;
+ }
+ }
+ ret = btf__load_into_kernel(btf);
+ if (ret) {
+ ALOGE("btf__load_into_kernel retry failed, errno: %d", errno);
+ return ret;
+ };
+ }
+ return 0;
+}
+
+int getKeyValueTids(const struct btf *btf, const char *mapName,
+ uint32_t expectedKeySize, uint32_t expectedValueSize,
+ uint32_t *keyTypeId, uint32_t *valueTypeId) {
+ const struct btf_type *kvBt;
+ const struct btf_member *key, *value;
+ const size_t max_name = 256;
+ char kvTypeName[max_name];
+ int64_t keySize, valueSize;
+ uint32_t kvId;
+
+ if (snprintf(kvTypeName, max_name, "____btf_map_%s", mapName) == max_name) {
+ ALOGE("____btf_map_%s is too long", mapName);
+ return -1;
+ }
+
+ kvId = btf__find_by_name(btf, kvTypeName);
+ if (kvId < 0) {
+ ALOGE("section not found, map: %s typeName: %s", mapName, kvTypeName);
+ return -1;
+ }
+
+ kvBt = btf__type_by_id(btf, kvId);
+ if (!kvBt) {
+ ALOGE("Couldn't find BTF type, map: %s id: %u", mapName, kvId);
+ return -1;
+ }
+
+ if (!btf_is_struct(kvBt) || btf_vlen(kvBt) < 2) {
+ ALOGE("Non Struct kind or invalid vlen, map: %s id: %u", mapName, kvId);
+ return -1;
+ }
+
+ key = btf_members(kvBt);
+ value = key + 1;
+
+ keySize = btf__resolve_size(btf, key->type);
+ if (keySize < 0) {
+ ALOGE("Couldn't get key size, map: %s errno: %d", mapName, errno);
+ return -1;
+ }
+
+ valueSize = btf__resolve_size(btf, value->type);
+ if (valueSize < 0) {
+ ALOGE("Couldn't get value size, map: %s errno: %d", mapName, errno);
+ return -1;
+ }
+
+ if (expectedKeySize != keySize || expectedValueSize != valueSize) {
+ ALOGE("Key value size mismatch, map: %s key size: %d expected key size: "
+ "%d value size: %d expected value size: %d",
+ mapName, (uint32_t)keySize, expectedKeySize, (uint32_t)valueSize,
+ expectedValueSize);
+ return -1;
+ }
+
+ *keyTypeId = key->type;
+ *valueTypeId = value->type;
+
+ return 0;
+}
+
+static bool isBtfSupported(enum bpf_map_type type) {
+ return type != BPF_MAP_TYPE_DEVMAP_HASH && type != BPF_MAP_TYPE_RINGBUF;
+}
+
static int createMaps(const char* elfPath, ifstream& elfFile, vector<unique_fd>& mapFds,
const char* prefix, const unsigned int bpfloader_ver) {
int ret;
- vector<char> mdData;
+ vector<char> mdData, btfData;
vector<struct bpf_map_def> md;
vector<string> mapNames;
string objName = pathToObjName(string(elfPath));
@@ -679,6 +856,25 @@
ret = getSectionSymNames(elfFile, "maps", mapNames);
if (ret) return ret;
+ struct btf *btf = NULL;
+ auto scopeGuard = base::make_scope_guard([btf] { if (btf) btf__free(btf); });
+ if (isAtLeastKernelVersion(4, 18, 0)) {
+ // On Linux Kernels older than 4.18 BPF_BTF_LOAD command doesn't exist.
+ ret = readSectionByName(".BTF", elfFile, btfData);
+ if (ret) {
+ ALOGE("Failed to read .BTF section, ret:%d", ret);
+ return ret;
+ }
+ struct btf *btf = btf__new(btfData.data(), btfData.size());
+ if (btf == NULL) {
+ ALOGE("btf__new failed, errno: %d", errno);
+ return -errno;
+ }
+
+ ret = loadBtf(elfFile, btf);
+ if (ret) return ret;
+ }
+
unsigned kvers = kernelVersion();
for (int i = 0; i < (int)mapNames.size(); i++) {
@@ -804,12 +1000,26 @@
};
if (isAtLeastKernelVersion(4, 15, 0))
strlcpy(req.map_name, mapNames[i].c_str(), sizeof(req.map_name));
+
+ bool haveBtf = btf && isBtfSupported(type);
+ if (haveBtf) {
+ uint32_t kTid, vTid;
+ ret = getKeyValueTids(btf, mapNames[i].c_str(), md[i].key_size,
+ md[i].value_size, &kTid, &vTid);
+ if (ret) return ret;
+ req.btf_fd = btf__fd(btf);
+ req.btf_key_type_id = kTid;
+ req.btf_value_type_id = vTid;
+ }
+
fd.reset(bpf(BPF_MAP_CREATE, req));
saved_errno = errno;
if (fd.ok()) {
- ALOGD("bpf_create_map[%s] -> %d", mapNames[i].c_str(), fd.get());
+ ALOGD("bpf_create_map[%s] btf:%d -> %d",
+ mapNames[i].c_str(), haveBtf, fd.get());
} else {
- ALOGE("bpf_create_map[%s] -> %d errno:%d", mapNames[i].c_str(), fd.get(), saved_errno);
+ ALOGE("bpf_create_map[%s] btf:%d -> %d errno:%d",
+ mapNames[i].c_str(), haveBtf, fd.get(), saved_errno);
}
}
@@ -1414,6 +1624,29 @@
return wear;
}
+static int libbpfPrint(enum libbpf_print_level lvl, const char *const formatStr,
+ va_list argList) {
+ int32_t prio;
+ switch (lvl) {
+ case LIBBPF_WARN:
+ prio = ANDROID_LOG_WARN;
+ break;
+ case LIBBPF_INFO:
+ prio = ANDROID_LOG_INFO;
+ break;
+ case LIBBPF_DEBUG:
+ prio = ANDROID_LOG_DEBUG;
+ break;
+ }
+ char *s = strdup(formatStr ?: "(no format string)");
+ int len = strlen(s);
+ if (len && s[len - 1] == '\n')
+ s[len - 1] = 0;
+ LOG_PRI_VA(prio, LOG_TAG, s, argList);
+ free(s);
+ return 0;
+}
+
static int doLoad(char** argv, char * const envp[]) {
if (!isAtLeastS) {
ALOGE("Impossible - not reachable on Android <S.");
@@ -1421,6 +1654,7 @@
// for any possible busted 'optimized' start everything vendor init hacks on R
return 0;
}
+ libbpf_set_print(libbpfPrint);
const bool runningAsRoot = !getuid(); // true iff U QPR3 or V+
diff --git a/clatd/clatd.h b/clatd/clatd.h
index daa5ebc..11e9687 100644
--- a/clatd/clatd.h
+++ b/clatd/clatd.h
@@ -48,7 +48,7 @@
// plus some extra just-in-case headroom, because it doesn't hurt.
#define MAXDUMPLEN (64 + MAXMTU)
-#define CLATD_VERSION "1.7"
+#define CLATD_VERSION "1.8"
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
diff --git a/clatd/main.c b/clatd/main.c
index fca996b..9e5710e 100644
--- a/clatd/main.c
+++ b/clatd/main.c
@@ -18,12 +18,17 @@
#include <arpa/inet.h>
#include <errno.h>
+#include <linux/audit.h>
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+#include <linux/unistd.h>
#include <netinet/in.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/personality.h>
+#include <sys/prctl.h>
#include <sys/utsname.h>
#include <unistd.h>
@@ -53,6 +58,131 @@
printf("-w [write socket descriptor number]\n");
}
+// Load the architecture identifier (AUDIT_ARCH_* constant)
+#define BPF_SECCOMP_LOAD_AUDIT_ARCH \
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, arch))
+
+// Load the system call number
+#define BPF_SECCOMP_LOAD_SYSCALL_NR \
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, nr))
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+// Load the system call argument n, where n is [0..5]
+#define BPF_SECCOMP_LOAD_SYSCALL_ARG_LO32(n) \
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[n]))
+#define BPF_SECCOMP_LOAD_SYSCALL_ARG_HI32(n) \
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, offsetof(struct seccomp_data, args[n]) + 4)
+#else
+#error "Not a little endian architecture?"
+#endif
+
+// Allow the system call
+#define BPF_SECCOMP_ALLOW BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_ALLOW)
+
+// Allow (but 'audit' log) the system call
+#define BPF_SECCOMP_LOG BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_LOG)
+
+// Reject the system call (kill thread)
+#define BPF_SECCOMP_KILL BPF_STMT(BPF_RET | BPF_K, SECCOMP_RET_KILL)
+
+// Note arguments to BPF_JUMP(opcode, operand, true_offset, false_offset)
+
+// If not equal, jump over count instructions
+#define BPF_JUMP_IF_NOT_EQUAL(v, count) \
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (v), 0, (count))
+
+// If equal, jump over count instructions
+#define BPF_JUMP_IF_EQUAL(v, count) \
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, (v), (count), 0)
+
+// *TWO* instructions: compare and if not equal jump over the allow statement
+#define BPF2_SECCOMP_ALLOW_IF_EQUAL(v) \
+ BPF_JUMP_IF_NOT_EQUAL((v), 1), \
+ BPF_SECCOMP_ALLOW
+
+// *TWO* instructions: compare and if not equal jump over the log statement
+#define BPF2_SECCOMP_LOG_IF_EQUAL(v) \
+ BPF_JUMP_IF_NOT_EQUAL((v), 1), \
+ BPF_SECCOMP_LOG
+
+// *TWO* instructions: compare and if equal jump over the kill statement
+#define BPF2_SECCOMP_KILL_IF_NOT_EQUAL(v) \
+ BPF_JUMP_IF_EQUAL((v), 1), \
+ BPF_SECCOMP_KILL
+
+// Android only supports the following 5 little endian architectures
+#if defined(__aarch64__) && defined(__LP64__)
+ #define MY_AUDIT_ARCH AUDIT_ARCH_AARCH64
+#elif defined(__arm__) && defined(__ILP32__)
+ #define MY_AUDIT_ARCH AUDIT_ARCH_ARM
+#elif defined(__i386__) && defined(__ILP32__)
+ #define MY_AUDIT_ARCH AUDIT_ARCH_I386
+#elif defined(__x86_64__) && defined(__LP64__)
+ #define MY_AUDIT_ARCH AUDIT_ARCH_X86_64
+#elif defined(__riscv) && defined(__LP64__)
+ #define MY_AUDIT_ARCH AUDIT_ARCH_RISCV64
+#else
+ #error "Unknown AUDIT_ARCH_* architecture."
+#endif
+
+void enable_seccomp(void) {
+ static const struct sock_filter filter[] = {
+ BPF_SECCOMP_LOAD_AUDIT_ARCH,
+ BPF2_SECCOMP_KILL_IF_NOT_EQUAL(MY_AUDIT_ARCH),
+
+ BPF_SECCOMP_LOAD_SYSCALL_NR, // aarch64
+
+ // main event loop:
+ // ppoll ( read sendmsg | recvmsg writev )
+ BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_ppoll), // 73
+ BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_read), // 63
+ BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_sendmsg), // 211
+ BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_recvmsg), // 212
+ BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_writev), // 66
+
+ // logging: getuid writev
+ BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_getuid), // 174
+
+ // inbound signal (SIGTERM) processing
+ BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_rt_sigreturn), // 139
+
+ // sleep(n)
+ BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_nanosleep), // 101
+
+ // _exit(0)
+ BPF2_SECCOMP_ALLOW_IF_EQUAL(__NR_exit_group), // 94
+
+#if defined(__aarch64__)
+ // Pixels are aarch64 - if we break clatd functionality on them,
+ // we *will* notice on GoogleGuest WiFi network (which is ipv6 only)
+ BPF_SECCOMP_KILL,
+#else
+ // All other architectures: generate audit lines visible in dmesg and logcat
+ BPF_SECCOMP_LOG,
+#endif
+ };
+ static const struct sock_fprog prog = {
+ .len = (unsigned short)ARRAY_SIZE(filter),
+ .filter = (struct sock_filter *)filter,
+ };
+
+ // https://man7.org/linux/man-pages/man2/PR_SET_NO_NEW_PRIVS.2const.html
+ // required to allow non-privileged seccomp filter installation
+ int rv = prctl(PR_SET_NO_NEW_PRIVS, 1L, 0L, 0L, 0L);
+ if (rv) {
+ logmsg(ANDROID_LOG_FATAL, "prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) = %d [%d]", rv, errno);
+ exit(1);
+ }
+
+ // https://man7.org/linux/man-pages/man2/PR_SET_SECCOMP.2const.html
+ // but see also https://man7.org/linux/man-pages/man2/seccomp.2.html
+ rv = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0L, 0L);
+ if (rv) {
+ logmsg(ANDROID_LOG_FATAL, "prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0) = %d [%d]", rv, errno);
+ exit(1);
+ }
+}
+
/* function: main
* allocate and setup the tun device, then run the event loop
*/
@@ -64,6 +194,48 @@
*write_sock_str = NULL;
unsigned len;
+ // Clatd binary is setuid/gid CLAT, thus when we reach here we have:
+ // $ adb shell ps | grep clat
+ // [pid] [ppid]
+ // clat 7650 1393 10785364 2612 do_sys_poll 0 S clatd-wlan0
+ // $ adb shell cat /proc/7650/status | egrep -i '^(Uid:|Gid:|Groups:)'
+ // [real][effective][saved][filesystem]
+ // [uid] [euid] [suid] [fsuid]
+ // Uid: 1000 1029 1029 1029
+ // [gid] [egid] [sgid] [fsgid]
+ // Gid: 1000 1029 1029 1029
+ // Groups: 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1018 1021 1023 1024 1032 1065 3001 3002 3003 3005 3006 3007 3009 3010 3011 3012
+ // This mismatch between uid & euid appears to cause periodic (every 5 minutes):
+ // objhash pid ppid uid
+ // W ActivityManager: Stale PhantomProcessRecord {xxxxxxx 7650:1393:clatd-wlan0/1000}, removing
+ // This is due to:
+ // $ adbz shell ls -ld /proc/7650
+ // dr-xr-xr-x 9 clat clat 0 2025-03-14 11:37 /proc/7650
+ // which is used by
+ // //frameworks/base/core/java/com/android/internal/os/ProcessCpuTracker.java
+ // which thus returns the uid 'clat' vs
+ // //frameworks/base/core/java/android/os/Process.java
+ // getUidForPid() which grabs *real* 'uid' from /proc/<pid>/status and is used in:
+ // //frameworks/base/services/core/java/com/android/server/am/PhantomProcessList.java
+ // (perhaps this should grab euid instead? unclear)
+ //
+ // However, we want to drop as many privs as possible, hence:
+ gid_t egid = getegid(); // documented to never fail, hence should return AID_CLAT == 1029
+ uid_t euid = geteuid(); // (ditto)
+ setresgid(egid, egid, egid); // ignore any failure
+ setresuid(euid, euid, euid); // ignore any failure
+ // ideally we'd somehow drop supplementary groups too...
+ // but for historical reasons that actually requires CAP_SETGID which we don't have
+ // (see man 2 setgroups)
+ //
+ // Now we (should) have:
+ // $ adb shell ps | grep clat
+ // clat 5370 1479 10785364 2528 do_sys_poll 0 S clatd-wlan0
+ // # adb shell cat /proc/5370/status | egrep -i '^(Uid:|Gid:|Groups:)'
+ // Uid: 1029 1029 1029 1029
+ // Gid: 1029 1029 1029 1029
+ // Groups: 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1018 1021 1023 1024 1032 1065 3001 3002 3003 3005 3006 3007 3009 3010 3011 3012
+
while ((opt = getopt(argc, argv, "i:p:4:6:t:r:w:h")) != -1) {
switch (opt) {
case 'i':
@@ -199,6 +371,8 @@
// TODO: actually perform true DAD
send_dad(tunnel.write_fd6, &Global_Clatd_Config.ipv6_local_subnet);
+ enable_seccomp(); // WARNING: from this point forward very limited system calls available.
+
event_loop(&tunnel);
if (sigterm) {
@@ -213,5 +387,7 @@
logmsg(ANDROID_LOG_INFO, "Clatd on %s %s SIGTERM", uplink_interface,
sigterm ? "received" : "timed out waiting for");
}
- return 0;
+
+ // Using _exit() here avoids 4 mprotect() syscalls triggered via 'exit(0)' or 'return 0'
+ _exit(0);
}
diff --git a/common/src/com/android/net/module/util/bpf/LocalNetAccessKey.java b/common/src/com/android/net/module/util/bpf/LocalNetAccessKey.java
index 95265b9..48e8b06 100644
--- a/common/src/com/android/net/module/util/bpf/LocalNetAccessKey.java
+++ b/common/src/com/android/net/module/util/bpf/LocalNetAccessKey.java
@@ -61,12 +61,46 @@
@Override
public String toString() {
- return "LocalNetAccessKey{"
- + "lpmBitlen=" + lpmBitlen
- + ", ifIndex=" + ifIndex
- + ", remoteAddress=" + remoteAddress
- + ", protocol=" + protocol
- + ", remotePort=" + remotePort
- + "}";
+ String s = "LocalNetAccessKey{lpmBitlen=" + lpmBitlen;
+
+ long bits = lpmBitlen;
+
+ // u32 ifIndex
+ if (bits <= 0 && ifIndex != 0) s += " ??";
+ if (bits > 0 || ifIndex != 0) s += " ifIndex=" + ifIndex;
+ if (bits > 0 && bits < 32) s += "/" + bits + "[LE]";
+ bits -= 32;
+
+ // u128 remoteAddress
+ if (bits <= 0 && !remoteAddress.isAnyLocalAddress()) s += " ??";
+ if (bits > 0 || !remoteAddress.isAnyLocalAddress()) {
+ s += " remoteAddress=";
+ String ip = remoteAddress.toString();
+ if (ip.startsWith("/::ffff:")) { // technically wrong IPv4-mapped IPv6 address detection
+ s += ip.substring(8);
+ if (bits >= 96 && bits < 128) s += "/" + (bits - 96);
+ } else if (ip.startsWith("/")) {
+ s += ip.substring(1);
+ if (bits >= 0 && bits < 128) s += "/" + bits;
+ } else { // WTF, includes a hostname or what?
+ s += ip;
+ }
+ }
+ bits -= 128;
+
+ // u16 protocol
+ if (bits <= 0 && protocol != 0) s += " ??";
+ if (bits > 0 || protocol != 0) s += " protocol=" + protocol;
+ if (bits > 0 && bits < 16) s += "/" + bits + "[LE16]";
+ bits -= 16;
+
+ // be16 remotePort
+ if (bits <= 0 && remotePort != 0) s += " ??";
+ if (bits > 0 || remotePort != 0) s += " remotePort=" + remotePort;
+ if (bits > 0 && bits < 16) s += "/" + bits + "[BE16]";
+ bits -= 16;
+
+ s += "}";
+ return s;
}
}
diff --git a/service/ServiceConnectivityResources/res/values/config_thread.xml b/service/ServiceConnectivityResources/res/values/config_thread.xml
index 128a98f..a458c7f 100644
--- a/service/ServiceConnectivityResources/res/values/config_thread.xml
+++ b/service/ServiceConnectivityResources/res/values/config_thread.xml
@@ -30,6 +30,15 @@
-->
<bool name="config_thread_border_router_default_enabled">false</bool>
+ <!-- Whether to enable or disable setting Thread country code from the telephony, wifi, location,
+ etc. The country code could be used by the Thread co-processor for setting the fixed output
+ power of Thread radio. If the device needs to dynamically change the max output power according
+ to the user scenario to meet the requirement of Specific Absorption Rate (SAR), it should call
+ the API `setChannelMaxPowers()` to change the max output power, and this configuration could be
+ set to false to disable the Thread service from setting the Thread country code.
+ -->
+ <bool name="config_thread_country_code_enabled">true</bool>
+
<!-- Whether to use location APIs in the algorithm to determine country code or not.
If disabled, will use other sources (telephony, wifi, etc) to determine device location for
Thread Network regulatory purposes.
diff --git a/service/src/com/android/server/BpfNetMaps.java b/service/src/com/android/server/BpfNetMaps.java
index 25c0617..523ffee 100644
--- a/service/src/com/android/server/BpfNetMaps.java
+++ b/service/src/com/android/server/BpfNetMaps.java
@@ -915,10 +915,13 @@
final InetAddress address, final int protocol, final int remotePort,
final boolean isAllowed) {
throwIfPre25Q2("addLocalNetAccess is not available on pre-B devices");
- final int ifIndex;
if (iface == null) {
- ifIndex = 0;
- } else {
+ Log.e(TAG, "Null iface, skip addLocalNetAccess for " + address);
+ return;
+ }
+ int ifIndex = mInterfaceTracker.getInterfaceIndex(iface);
+ if (ifIndex == 0) {
+ mInterfaceTracker.addInterface(iface);
ifIndex = mInterfaceTracker.getInterfaceIndex(iface);
}
if (ifIndex == 0) {
@@ -1324,12 +1327,12 @@
+ value.iif1 + "(" + mDeps.getIfName(value.iif1) + "), "
+ value.iif2 + "(" + mDeps.getIfName(value.iif2) + ")");
if (sLocalNetBlockedUidMap != null) {
- BpfDump.dumpMap(sLocalNetAccessMap, pw, "sLocalNetAccessMap",
- (key, value) -> "[" + key + "]: " + value);
+ BpfDump.dumpMap(sLocalNetAccessMap, pw, "sLocalNetAccessMap (default is true meaning global)",
+ (key, value) -> "" + key + ": " + value.val);
}
if (sLocalNetBlockedUidMap != null) {
BpfDump.dumpMap(sLocalNetBlockedUidMap, pw, "sLocalNetBlockedUidMap",
- (key, value) -> "[" + key + "]: " + value);
+ (key, value) -> "" + key + ": " + value.val);
}
dumpDataSaverConfig(pw);
pw.decreaseIndent();
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index dc4a35b..bfb51da 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -10005,10 +10005,12 @@
// Adds dns allow rule to LocalNetAccessMap for both TCP and UDP protocol at port 53,
// if it is a local dns (ie. it falls in the local prefix range).
if (prefix.contains(dnsServer)) {
- mBpfNetMaps.addLocalNetAccess(getIpv4MappedAddressBitLen(), iface, dnsServer,
+ mBpfNetMaps.addLocalNetAccess(32 + 128 + 16 + 16, iface, dnsServer,
IPPROTO_UDP, 53, true);
- mBpfNetMaps.addLocalNetAccess(getIpv4MappedAddressBitLen(), iface, dnsServer,
+ mBpfNetMaps.addLocalNetAccess(32 + 128 + 16 + 16, iface, dnsServer,
IPPROTO_TCP, 53, true);
+ mBpfNetMaps.addLocalNetAccess(32 + 128 + 16 + 16, iface, dnsServer,
+ IPPROTO_TCP, 853, true); // DNS over TLS
}
}
}
@@ -10027,25 +10029,17 @@
// Removes dns allow rule from LocalNetAccessMap for both TCP and UDP protocol
// at port 53, if it is a local dns (ie. it falls in the prefix range).
if (prefix.contains(dnsServer)) {
- mBpfNetMaps.removeLocalNetAccess(getIpv4MappedAddressBitLen(), iface, dnsServer,
+ mBpfNetMaps.removeLocalNetAccess(32 + 128 + 16 + 16, iface, dnsServer,
IPPROTO_UDP, 53);
- mBpfNetMaps.removeLocalNetAccess(getIpv4MappedAddressBitLen(), iface, dnsServer,
+ mBpfNetMaps.removeLocalNetAccess(32 + 128 + 16 + 16, iface, dnsServer,
IPPROTO_TCP, 53);
+ mBpfNetMaps.removeLocalNetAccess(32 + 128 + 16 + 16, iface, dnsServer,
+ IPPROTO_TCP, 853); // DNS over TLS
}
}
}
/**
- * Returns total bit length of an Ipv4 mapped address.
- */
- private int getIpv4MappedAddressBitLen() {
- final int ifaceLen = 32; // bit length of interface
- final int inetAddressLen = 32 + 96; // length of ipv4 mapped addresses
- final int portProtocolLen = 32; //16 for port + 16 for protocol;
- return ifaceLen + inetAddressLen + portProtocolLen;
- }
-
- /**
* Have netd update routes from oldLp to newLp.
* @return true if routes changed between oldLp and newLp
*/
diff --git a/service/src/com/android/server/connectivity/DscpPolicyTracker.java b/service/src/com/android/server/connectivity/DscpPolicyTracker.java
index 9c2b9e8..857d705 100644
--- a/service/src/com/android/server/connectivity/DscpPolicyTracker.java
+++ b/service/src/com/android/server/connectivity/DscpPolicyTracker.java
@@ -233,6 +233,11 @@
*/
public void addDscpPolicy(NetworkAgentInfo nai, DscpPolicy policy) {
String iface = nai.linkProperties.getInterfaceName();
+ if (null == iface) {
+ Log.e(TAG, "DSCP policies are not supported on null interfaces.");
+ sendStatus(nai, policy.getPolicyId(), DSCP_POLICY_STATUS_REQUEST_DECLINED);
+ return;
+ }
if (!isEthernet(iface)) {
Log.e(TAG, "DSCP policies are not supported on raw IP interfaces.");
sendStatus(nai, policy.getPolicyId(), DSCP_POLICY_STATUS_REQUEST_DECLINED);
diff --git a/staticlibs/device/com/android/net/module/util/TcUtils.java b/staticlibs/device/com/android/net/module/util/TcUtils.java
index a6b222f..eb119c8 100644
--- a/staticlibs/device/com/android/net/module/util/TcUtils.java
+++ b/staticlibs/device/com/android/net/module/util/TcUtils.java
@@ -16,6 +16,8 @@
package com.android.net.module.util;
+import androidx.annotation.NonNull;
+
import java.io.IOException;
/**
@@ -33,7 +35,7 @@
* @return true if the interface uses an ethernet L2 header.
* @throws IOException
*/
- public static native boolean isEthernet(String iface) throws IOException;
+ public static native boolean isEthernet(@NonNull String iface) throws IOException;
/**
* Attach a tc bpf filter.
diff --git a/staticlibs/framework/com/android/net/module/util/SdkUtil.java b/staticlibs/framework/com/android/net/module/util/SdkUtil.java
index 5006ba9..63558dd 100644
--- a/staticlibs/framework/com/android/net/module/util/SdkUtil.java
+++ b/staticlibs/framework/com/android/net/module/util/SdkUtil.java
@@ -16,7 +16,10 @@
package com.android.net.module.util;
+import static android.os.Build.VERSION.SDK_INT;
+
import android.annotation.Nullable;
+import android.os.Build;
/**
* Utilities to deal with multiple SDKs in a single mainline module.
@@ -46,4 +49,9 @@
this.value = value;
}
}
+
+ /** Checks if the device is running on a release version of Android Baklava or newer */
+ public static boolean isAtLeast25Q2() {
+ return SDK_INT >= 36 || (SDK_INT == 35 && "Baklava".equals(Build.VERSION.CODENAME));
+ }
}
diff --git a/staticlibs/native/bpfmapjni/com_android_net_module_util_TcUtils.cpp b/staticlibs/native/bpfmapjni/com_android_net_module_util_TcUtils.cpp
index 2a587b6..22b084c 100644
--- a/staticlibs/native/bpfmapjni/com_android_net_module_util_TcUtils.cpp
+++ b/staticlibs/native/bpfmapjni/com_android_net_module_util_TcUtils.cpp
@@ -32,6 +32,10 @@
static jboolean com_android_net_module_util_TcUtils_isEthernet(JNIEnv *env,
jclass clazz,
jstring iface) {
+ if (nullptr == iface) {
+ jniThrowNullPointerException(env, "iface is null");
+ return false;
+ }
ScopedUtfChars interface(env, iface);
bool result = false;
int error = isEthernet(interface.c_str(), result);
diff --git a/staticlibs/tests/unit/host/python/apf_utils_test.py b/staticlibs/tests/unit/host/python/apf_utils_test.py
index d4753b7..55fbe58 100644
--- a/staticlibs/tests/unit/host/python/apf_utils_test.py
+++ b/staticlibs/tests/unit/host/python/apf_utils_test.py
@@ -27,6 +27,7 @@
get_apf_counters_from_dumpsys,
get_ipv4_addresses,
get_non_tentative_ipv6_addresses,
+ get_exclude_all_host_ipv6_multicast_addresses,
get_hardware_address,
is_send_raw_packet_downstream_supported,
is_packet_capture_supported,
@@ -170,6 +171,31 @@
ip_addresses = get_non_tentative_ipv6_addresses(self.mock_ad, "wlan0")
asserts.assert_equal(ip_addresses, [])
+
+ @patch("net_tests_utils.host.python.adb_utils.adb_shell")
+ def test_get_exclude_all_host_ipv6_multicast_addresses_success(
+ self, mock_adb_shell: MagicMock
+ ) -> None:
+ mock_adb_shell.return_value = """
+47: wlan0
+ inet6 ff02::1:ff99:37b0
+ inet6 ff02::1:ffb7:cba2 users 2
+ inet6 ff02::1
+ inet6 ff01::1
+"""
+ ip_addresses = get_exclude_all_host_ipv6_multicast_addresses(self.mock_ad, "wlan0")
+ asserts.assert_equal(ip_addresses,
+ ["ff02::1:ff99:37b0",
+ "ff02::1:ffb7:cba2"])
+
+ @patch("net_tests_utils.host.python.adb_utils.adb_shell")
+ def test_get_exclude_all_host_ipv6_multicast_addresses_not_found(
+ self, mock_adb_shell: MagicMock
+ ) -> None:
+ mock_adb_shell.return_value = ""
+ ip_addresses = get_exclude_all_host_ipv6_multicast_addresses(self.mock_ad, "wlan0")
+ asserts.assert_equal(ip_addresses, [])
+
@patch("net_tests_utils.host.python.adb_utils.adb_shell")
def test_send_raw_packet_downstream_success(
self, mock_adb_shell: MagicMock
diff --git a/staticlibs/testutils/host/python/apf_utils.py b/staticlibs/testutils/host/python/apf_utils.py
index 1648d36..fa8a1da 100644
--- a/staticlibs/testutils/host/python/apf_utils.py
+++ b/staticlibs/testutils/host/python/apf_utils.py
@@ -148,6 +148,38 @@
else:
return []
+def get_exclude_all_host_ipv6_multicast_addresses(
+ ad: android_device.AndroidDevice, iface_name: str
+) -> list[str]:
+ """Retrieves the IPv6 multicast addresses of a given interface on an Android device.
+
+ This function executes an ADB shell command (`ip -6 maddr show`) to get the
+ network interface information and extracts the IPv6 multicast address from the output.
+ If devices have no IPv6 multicast address, raise PatternNotFoundException.
+
+ Args:
+ ad: The Android device object.
+ iface_name: The name of the network interface (e.g., "wlan0").
+
+ Returns:
+ The IPv6 multicast addresses of the interface as a list of string.
+ Return empty list if no IPv6 multicast address.
+ """
+ # output format
+ # 47: wlan0
+ # inet6 ff02::1:ff99:37b0
+ # inet6 ff02::1:ffb7:cba2 users 2
+ # inet6 ff02::1
+ # inet6 ff01::1
+ output = adb_utils.adb_shell(ad, f"ip -6 maddr show {iface_name}")
+ pattern = r"inet6\s+([a-fA-F0-9:]+)(?:\s+users\s+\d+)?"
+ matches = re.findall(pattern, output)
+
+ if matches:
+ return [addr for addr in matches if addr not in ("ff02::1", "ff01::1")]
+ else:
+ return []
+
def get_hardware_address(
ad: android_device.AndroidDevice, iface_name: str
) -> str:
@@ -410,7 +442,25 @@
"client device is not B+"
)
- asserts.abort_class_if(not self.client.isAtLeastB(), "not B+")
+ asserts.skip_if(not self.client.isAtLeastB(), "not B+")
+ return test_function(self, *args, **kwargs)
+ return wrapper
+ return decorator
+
+def apf_ram_at_least(size):
+ def decorator(test_function):
+ @functools.wraps(test_function)
+ def wrapper(self, *args, **kwargs):
+ asserts.abort_class_if(
+ (not hasattr(self, 'clientDevice')) or (not hasattr(self, 'client_iface_name')),
+ "no valid client attribute"
+ )
+
+ caps = get_apf_capabilities(self.clientDevice, self.client_iface_name)
+ asserts.skip_if(
+ caps.apf_ram_size < size,
+ f'APF rame size {caps.apf_ram_size} < {size}'
+ )
return test_function(self, *args, **kwargs)
return wrapper
return decorator
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/TetheringTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/TetheringTest.java
index a1cf968..65844a3 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/TetheringTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/TetheringTest.java
@@ -23,6 +23,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assume.assumeTrue;
import android.content.Context;
import android.net.TetheringInterface;
@@ -31,6 +32,8 @@
import android.net.wifi.SoftApConfiguration;
import android.net.wifi.WifiSsid;
+import com.android.modules.utils.build.SdkLevel;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -64,6 +67,8 @@
*/
@Test
public void testSoftApConfigurationRedactedForOtherUids() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastB());
+
mTetheringEventCallback.assumeWifiTetheringSupported(
getInstrumentation().getTargetContext());
SoftApConfiguration softApConfig = new SoftApConfiguration.Builder()
diff --git a/tests/cts/multidevices/apfv6_test.py b/tests/cts/multidevices/apfv6_test.py
index b82a3be..fb45f4a 100644
--- a/tests/cts/multidevices/apfv6_test.py
+++ b/tests/cts/multidevices/apfv6_test.py
@@ -14,6 +14,7 @@
from mobly import asserts
from scapy.layers.inet import IP, ICMP, IPOption_Router_Alert
+from scapy.layers.inet6 import IPv6, ICMPv6EchoRequest, ICMPv6EchoReply
from scapy.layers.l2 import Ether
from scapy.contrib.igmpv3 import IGMPv3, IGMPv3mq, IGMPv3mr, IGMPv3gr
from net_tests_utils.host.python import apf_test_base, apf_utils, adb_utils, assert_utils, packet_utils
@@ -102,6 +103,22 @@
)
@apf_utils.at_least_B()
+ def test_ipv6_icmp_echo_request_offload(self):
+ eth = Ether(src=self.server_mac_address, dst=self.client_mac_address)
+ ip = IPv6(src=self.server_ipv6_addresses[0], dst=self.client_ipv6_addresses[0])
+ icmp = ICMPv6EchoRequest(id=1, seq=123)
+ echo_request = bytes(eth/ip/icmp/b"hello").hex()
+
+ eth = Ether(src=self.client_mac_address, dst=self.server_mac_address)
+ ip = IPv6(src=self.client_ipv6_addresses[0], dst=self.server_ipv6_addresses[0])
+ icmp = ICMPv6EchoReply(id=1, seq=123)
+ expected_echo_reply = bytes(eth/ip/icmp/b"hello").hex()
+
+ self.send_packet_and_expect_reply_received(
+ echo_request, "DROPPED_IPV6_ICMP6_ECHO_REQUEST_REPLIED", expected_echo_reply
+ )
+
+ @apf_utils.at_least_B()
def test_igmpv3_general_query_offload(self):
ether = Ether(src=self.server_mac_address, dst='01:00:5e:00:00:01')
ip = IP(
diff --git a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
index 81afabc..7dbb9b2 100644
--- a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
+++ b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
@@ -372,7 +372,7 @@
if (caps.apfVersionSupported > 4) {
assertThat(caps.maximumApfProgramSize).isAtLeast(2048)
- assertThat(caps.apfVersionSupported).isEqualTo(6000) // v6.0000
+ assertThat(caps.apfVersionSupported).isAnyOf(6000, 6100) // v6.000 or v6.100
}
// DEVICEs launching with Android 15 (AOSP experimental) or higher with CHIPSETs that set
@@ -383,14 +383,22 @@
assertThat(caps.maximumApfProgramSize).isAtLeast(2048)
}
- // CHIPSETs (or DEVICES with CHIPSETs) that set ro.board.first_api_level or
- // ro.board.api_level to 202504 or higher:
- // - [VSR-5.3.12-018] MUST implement version 6 of the Android Packet Filtering (APF)
- // interpreter in the Wi-Fi firmware.
- // - [VSR-5.3.12-019] MUST provide at least 4000 bytes of APF RAM.
+ // DEVICEs with CHIPSETs that set ro.board.first_api_level or ro.board.api_level to 202504
+ // or higher:
+ // - [VSR-5.3.12-018] MUST implement version 6 or version 6.1 of the Android Packet
+ // Filtering (APF) interpreter in the Wi-Fi firmware.
+ // - [VSR-5.3.12-019] MUST provide at least 4000 bytes of APF RAM when version 6 is
+ // implemented OR 3000 bytes when version 6.1 is implemented.
+ // - Note, the APF RAM requirement for APF version 6.1 will become 4000 bytes in Android 17
+ // with CHIPSETs that set ro.board.first_api_level or ro.board.api_level to 202604 or
+ // higher.
if (vsrApiLevel >= 202504) {
- assertThat(caps.apfVersionSupported).isEqualTo(6000)
- assertThat(caps.maximumApfProgramSize).isAtLeast(4000)
+ assertThat(caps.apfVersionSupported).isAnyOf(6000, 6100)
+ if (caps.apfVersionSupported == 6000) {
+ assertThat(caps.maximumApfProgramSize).isAtLeast(4000)
+ } else {
+ assertThat(caps.maximumApfProgramSize).isAtLeast(3000)
+ }
}
// ApfFilter does not support anything but ARPHRD_ETHER.
diff --git a/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt b/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
index ceccf0b..df4dab5 100644
--- a/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
+++ b/tests/cts/net/src/android/net/cts/DscpPolicyTest.kt
@@ -757,6 +757,25 @@
assertEquals(IPPROTO_UDP, policy2.protocol)
assertParcelingIsLossless(policy2)
}
+
+ @Test
+ fun testSendDscpPolicyWithoutInterfaceName() {
+ val nc = NetworkCapabilities().apply {
+ addTransportType(TRANSPORT_TEST)
+ }
+ val agent = TestableNetworkAgent(
+ realContext,
+ handlerThread.looper,
+ nc,
+ LinkProperties() /* note: no interface name */,
+ NetworkAgentConfig.Builder().build()
+ )
+ agentsToCleanUp.add(agent)
+ runAsShell(MANAGE_TEST_NETWORKS) { agent.register() }
+ // Without the fix, this will crash the system with SIGSEGV.
+ agent.sendAddDscpPolicy(DscpPolicy.Builder(1, 1).build())
+ agent.expectCallback<OnDscpPolicyStatusUpdated>()
+ }
}
private fun ByteBuffer.readAsArray(): ByteArray {
diff --git a/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java b/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
index 27cba3a..3b8f5bc 100644
--- a/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
+++ b/tests/cts/net/util/java/android/net/cts/util/CtsTetheringUtils.java
@@ -119,7 +119,10 @@
cv instanceof CallbackValue.OnTetheringStarted);
}
- public void expectTetheringFailed(final int expected) throws InterruptedException {
+ /**
+ * Verify that starting tethering failed with the specified error code.
+ */
+ public void expectTetheringFailed(final int expected) {
final CallbackValue cv = mHistory.poll(TIMEOUT_MS, c -> true);
assertNotNull("No onTetheringFailed after " + TIMEOUT_MS + " ms", cv);
assertTrue("Expect fail with error code " + expected + ", but received: " + cv,
@@ -559,19 +562,28 @@
}
/**
- * Starts Wi-Fi tethering.
+ * Starts Wi-Fi tethering with TETHER_PRIVILEGED permission.
*/
- public TetheringInterface startWifiTethering(final TestTetheringEventCallback callback)
- throws InterruptedException {
+ public TetheringInterface startWifiTethering(final TestTetheringEventCallback callback) {
return startWifiTethering(callback, null);
}
/**
- * Starts Wi-Fi tethering with the specified SoftApConfiguration.
+ * Starts Wi-Fi tethering with TETHER_PRIVILEGED permission and the specified
+ * SoftApConfiguration.
*/
public TetheringInterface startWifiTethering(final TestTetheringEventCallback callback,
- final SoftApConfiguration softApConfiguration)
- throws InterruptedException {
+ final SoftApConfiguration softApConfiguration) {
+ return runAsShell(TETHER_PRIVILEGED, () -> startWifiTetheringNoPermissions(
+ callback, softApConfiguration));
+ }
+
+ /**
+ * Starts Wi-Fi tethering without any permission with the specified SoftApConfiguration.
+ */
+ public TetheringInterface startWifiTetheringNoPermissions(
+ final TestTetheringEventCallback callback,
+ final SoftApConfiguration softApConfiguration) {
final List<String> wifiRegexs = getWifiTetherableInterfaceRegexps(callback);
final StartTetheringCallback startTetheringCallback = new StartTetheringCallback();
@@ -582,19 +594,17 @@
}
final TetheringRequest request = builder.build();
- return runAsShell(TETHER_PRIVILEGED, () -> {
- mTm.startTethering(request, c -> c.run() /* executor */, startTetheringCallback);
- startTetheringCallback.verifyTetheringStarted();
+ mTm.startTethering(request, c -> c.run() /* executor */, startTetheringCallback);
+ startTetheringCallback.verifyTetheringStarted();
- final TetheringInterface iface =
- callback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI);
+ final TetheringInterface iface =
+ callback.expectTetheredInterfacesChanged(wifiRegexs, TETHERING_WIFI);
- callback.expectOneOfOffloadStatusChanged(
- TETHER_HARDWARE_OFFLOAD_STARTED,
- TETHER_HARDWARE_OFFLOAD_FAILED);
+ callback.expectOneOfOffloadStatusChanged(
+ TETHER_HARDWARE_OFFLOAD_STARTED,
+ TETHER_HARDWARE_OFFLOAD_FAILED);
- return iface;
- });
+ return iface;
}
private static class StopSoftApCallback implements SoftApCallback {
diff --git a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
index 7d6a213..abe628b 100644
--- a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
+++ b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
@@ -487,6 +487,9 @@
final TestTetheringEventCallback tetherEventCallback =
mCtsTetheringUtils.registerTetheringEventCallback();
try {
+ tetherEventCallback.assumeWifiTetheringSupported(mContext);
+ tetherEventCallback.expectNoTetheringActive();
+
final StartTetheringCallback startTetheringCallback = new StartTetheringCallback();
mTM.startTethering(new TetheringRequest.Builder(TETHERING_VIRTUAL).build(),
c -> c.run(), startTetheringCallback);
@@ -508,6 +511,7 @@
mCtsTetheringUtils.registerTetheringEventCallback();
try {
tetherEventCallback.assumeWifiTetheringSupported(mContext);
+ tetherEventCallback.expectNoTetheringActive();
SoftApConfiguration softApConfig = new SoftApConfiguration.Builder()
.setWifiSsid(WifiSsid.fromBytes("This is one config"
@@ -532,6 +536,7 @@
mCtsTetheringUtils.registerTetheringEventCallback();
try {
tetherEventCallback.assumeWifiTetheringSupported(mContext);
+ tetherEventCallback.expectNoTetheringActive();
SoftApConfiguration softApConfig = new SoftApConfiguration.Builder()
.setWifiSsid(WifiSsid.fromBytes("This is one config"
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt b/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt
index 94c68c0..4aeae19 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSKeepConnectedTest.kt
@@ -45,20 +45,29 @@
.addTransportType(TRANSPORT_WIFI)
.addCapability(NET_CAPABILITY_LOCAL_NETWORK)
.build()
- val keepConnectedAgent = Agent(nc = nc, score = FromS(NetworkScore.Builder()
- .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
- .build()),
- lnc = FromS(LocalNetworkConfig.Builder().build()))
- val dontKeepConnectedAgent = Agent(nc = nc,
- lnc = FromS(LocalNetworkConfig.Builder().build()))
+ val keepConnectedAgent = Agent(
+ nc = nc,
+ score = FromS(
+ NetworkScore.Builder()
+ .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
+ .build()
+ ),
+ lnc = FromS(LocalNetworkConfig.Builder().build())
+ )
+ val dontKeepConnectedAgent = Agent(
+ nc = nc,
+ lnc = FromS(LocalNetworkConfig.Builder().build())
+ )
doTestKeepConnected(keepConnectedAgent, dontKeepConnectedAgent)
}
@Test
fun testKeepConnectedForTest() {
- val keepAgent = Agent(score = FromS(NetworkScore.Builder()
- .setKeepConnectedReason(KEEP_CONNECTED_FOR_TEST)
- .build()))
+ val keepAgent = Agent(score = FromS(
+ NetworkScore.Builder()
+ .setKeepConnectedReason(KEEP_CONNECTED_FOR_TEST)
+ .build()
+ ))
val dontKeepAgent = Agent()
doTestKeepConnected(keepAgent, dontKeepAgent)
}
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentCreationTests.kt b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentCreationTests.kt
index 16a30aa..6805d9a 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentCreationTests.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentCreationTests.kt
@@ -61,7 +61,8 @@
data class TestParams(
val sdkLevel: Int,
val isTv: Boolean = false,
- val addLocalNetCapToRequest: Boolean = true)
+ val addLocalNetCapToRequest: Boolean = true
+ )
companion object {
@JvmStatic
@@ -81,8 +82,14 @@
}
private fun makeNativeNetworkConfigLocal(netId: Int, permission: Int) =
- NativeNetworkConfig(netId, NativeNetworkType.PHYSICAL_LOCAL, permission,
- false /* secure */, VpnManager.TYPE_VPN_NONE, false /* excludeLocalRoutes */)
+ NativeNetworkConfig(
+ netId,
+ NativeNetworkType.PHYSICAL_LOCAL,
+ permission,
+ false /* secure */,
+ VpnManager.TYPE_VPN_NONE,
+ false /* excludeLocalRoutes */
+ )
@Test
fun testLocalAgents() {
@@ -99,8 +106,8 @@
addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
addCapability(NET_CAPABILITY_LOCAL_NETWORK)
}.build()
- val localAgent = if (params.sdkLevel >= VERSION_V
- || params.sdkLevel == VERSION_U && params.isTv) {
+ val localAgent = if (params.sdkLevel >= VERSION_V ||
+ params.sdkLevel == VERSION_U && params.isTv) {
Agent(nc = ncTemplate, score = keepConnectedScore(), lnc = defaultLnc())
} else {
assertFailsWith<IllegalArgumentException> { Agent(nc = ncTemplate, lnc = defaultLnc()) }
@@ -109,7 +116,8 @@
}
localAgent.connect()
netdInOrder.verify(netd).networkCreate(
- makeNativeNetworkConfigLocal(localAgent.network.netId, INetd.PERMISSION_NONE))
+ makeNativeNetworkConfigLocal(localAgent.network.netId, INetd.PERMISSION_NONE)
+ )
if (params.addLocalNetCapToRequest) {
assertEquals(localAgent.network, allNetworksCb.expect<Available>().network)
} else {
@@ -123,10 +131,12 @@
@Test
fun testBadAgents() {
assertFailsWith<IllegalArgumentException> {
- Agent(nc = NetworkCapabilities.Builder()
- .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
- .build(),
- lnc = null)
+ Agent(
+ nc = NetworkCapabilities.Builder()
+ .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+ .build(),
+ lnc = null
+ )
}
assertFailsWith<IllegalArgumentException> {
Agent(nc = NetworkCapabilities.Builder().build(), lnc = defaultLnc())
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt
index 3583f84..d835155 100644
--- a/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/CSLocalAgentTests.kt
@@ -111,14 +111,18 @@
deps.setBuildSdk(VERSION_V)
assertFailsWith<IllegalArgumentException> {
- Agent(nc = NetworkCapabilities.Builder()
- .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
- .build(),
- lnc = null)
+ Agent(
+ nc = NetworkCapabilities.Builder()
+ .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+ .build(),
+ lnc = null
+ )
}
assertFailsWith<IllegalArgumentException> {
- Agent(nc = NetworkCapabilities.Builder().build(),
- lnc = FromS(LocalNetworkConfig.Builder().build()))
+ Agent(
+ nc = NetworkCapabilities.Builder().build(),
+ lnc = FromS(LocalNetworkConfig.Builder().build())
+ )
}
}
@@ -127,27 +131,31 @@
deps.setBuildSdk(VERSION_V)
val cb = TestableNetworkCallback()
- cm.requestNetwork(NetworkRequest.Builder()
- .clearCapabilities()
- .build(),
- cb)
- val agent = Agent(nc = NetworkCapabilities.Builder()
- .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
- .build(),
- lnc = FromS(LocalNetworkConfig.Builder().build()))
+ cm.requestNetwork(
+ NetworkRequest.Builder()
+ .clearCapabilities()
+ .build(),
+ cb
+ )
+ val agent = Agent(
+ nc = NetworkCapabilities.Builder()
+ .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+ .build(),
+ lnc = FromS(LocalNetworkConfig.Builder().build())
+ )
agent.connect()
cb.expectAvailableCallbacks(agent.network, validated = false)
agent.sendNetworkCapabilities(NetworkCapabilities.Builder().build())
cb.expect<Lost>(agent.network)
- val agent2 = Agent(nc = NetworkCapabilities.Builder()
- .build(),
- lnc = null)
+ val agent2 = Agent(nc = NetworkCapabilities.Builder().build(), lnc = null)
agent2.connect()
cb.expectAvailableCallbacks(agent2.network, validated = false)
- agent2.sendNetworkCapabilities(NetworkCapabilities.Builder()
- .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
- .build())
+ agent2.sendNetworkCapabilities(
+ NetworkCapabilities.Builder()
+ .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+ .build()
+ )
cb.expect<Lost>(agent2.network)
}
@@ -156,10 +164,12 @@
deps.setBuildSdk(VERSION_V)
val cb = TestableNetworkCallback()
- cm.requestNetwork(NetworkRequest.Builder()
- .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
- .build(),
- cb)
+ cm.requestNetwork(
+ NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+ .build(),
+ cb
+ )
// Set up a local agent that should forward its traffic to the best DUN upstream.
val localAgent = Agent(
@@ -171,15 +181,20 @@
cb.expectAvailableCallbacks(localAgent.network, validated = false)
- val wifiAgent = Agent(score = keepScore(), lp = lp("wifi0"),
- nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
+ val wifiAgent = Agent(
+ score = keepScore(),
+ lp = lp("wifi0"),
+ nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET)
+ )
wifiAgent.connect()
val newLnc = LocalNetworkConfig.Builder()
- .setUpstreamSelector(NetworkRequest.Builder()
- .addTransportType(TRANSPORT_WIFI)
- .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
- .build())
+ .setUpstreamSelector(
+ NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
+ .build()
+ )
.build()
localAgent.sendLocalNetworkConfig(newLnc)
@@ -207,21 +222,29 @@
nc = nc(TRANSPORT_THREAD, NET_CAPABILITY_LOCAL_NETWORK),
lp = lp(name),
lnc = localNetworkConfig,
- score = FromS(NetworkScore.Builder()
+ score = FromS(
+ NetworkScore.Builder()
.setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
- .build())
+ .build()
+ )
)
return localAgent
}
private fun createWifiAgent(name: String): CSAgentWrapper {
- return Agent(score = keepScore(), lp = lp(name),
- nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
+ return Agent(
+ score = keepScore(),
+ lp = lp(name),
+ nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET)
+ )
}
private fun createCellAgent(name: String): CSAgentWrapper {
- return Agent(score = keepScore(), lp = lp(name),
- nc = nc(TRANSPORT_CELLULAR, NET_CAPABILITY_INTERNET))
+ return Agent(
+ score = keepScore(),
+ lp = lp(name),
+ nc = nc(TRANSPORT_CELLULAR, NET_CAPABILITY_INTERNET)
+ )
}
private fun sendLocalNetworkConfig(
@@ -245,11 +268,12 @@
cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
val inOrder = inOrder(multicastRoutingCoordinatorService)
- val lnc = FromS(LocalNetworkConfig.Builder()
- .setUpstreamSelector(upstreamSelectorWifi)
- .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
- .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
- .build()
+ val lnc = FromS(
+ LocalNetworkConfig.Builder()
+ .setUpstreamSelector(upstreamSelectorWifi)
+ .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
+ .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
+ .build()
)
val localAgent = createLocalAgent("local0", lnc)
localAgent.connect()
@@ -264,9 +288,15 @@
}
inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
- "local0", "wifi0", multicastRoutingConfigMinScope)
+ "local0",
+ "wifi0",
+ multicastRoutingConfigMinScope
+ )
inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
- "wifi0", "local0", multicastRoutingConfigSelected)
+ "wifi0",
+ "local0",
+ multicastRoutingConfigSelected
+ )
wifiAgent.disconnect()
@@ -282,11 +312,12 @@
fun testMulticastRoutingConfig_2LocalNetworks() {
deps.setBuildSdk(VERSION_V)
val inOrder = inOrder(multicastRoutingCoordinatorService)
- val lnc = FromS(LocalNetworkConfig.Builder()
- .setUpstreamSelector(upstreamSelectorWifi)
- .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
- .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
- .build()
+ val lnc = FromS(
+ LocalNetworkConfig.Builder()
+ .setUpstreamSelector(upstreamSelectorWifi)
+ .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
+ .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
+ .build()
)
val localAgent0 = createLocalAgent("local0", lnc)
localAgent0.connect()
@@ -296,18 +327,30 @@
waitForIdle()
inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
- "local0", "wifi0", multicastRoutingConfigMinScope)
+ "local0",
+ "wifi0",
+ multicastRoutingConfigMinScope
+ )
inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
- "wifi0", "local0", multicastRoutingConfigSelected)
+ "wifi0",
+ "local0",
+ multicastRoutingConfigSelected
+ )
val localAgent1 = createLocalAgent("local1", lnc)
localAgent1.connect()
waitForIdle()
inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
- "local1", "wifi0", multicastRoutingConfigMinScope)
+ "local1",
+ "wifi0",
+ multicastRoutingConfigMinScope
+ )
inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
- "wifi0", "local1", multicastRoutingConfigSelected)
+ "wifi0",
+ "local1",
+ multicastRoutingConfigSelected
+ )
localAgent0.disconnect()
localAgent1.disconnect()
@@ -318,15 +361,19 @@
fun testMulticastRoutingConfig_UpstreamNetworkCellToWifi() {
deps.setBuildSdk(VERSION_V)
val cb = TestableNetworkCallback()
- cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities()
+ cm.registerNetworkCallback(
+ NetworkRequest.Builder().clearCapabilities()
.addCapability(NET_CAPABILITY_LOCAL_NETWORK)
- .build(), cb)
+ .build(),
+ cb
+ )
val inOrder = inOrder(multicastRoutingCoordinatorService)
- val lnc = FromS(LocalNetworkConfig.Builder()
- .setUpstreamSelector(upstreamSelectorAny)
- .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
- .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
- .build()
+ val lnc = FromS(
+ LocalNetworkConfig.Builder()
+ .setUpstreamSelector(upstreamSelectorAny)
+ .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
+ .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
+ .build()
)
val localAgent = createLocalAgent("local0", lnc)
val wifiAgent = createWifiAgent("wifi0")
@@ -341,9 +388,15 @@
}
inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
- "local0", "cell0", multicastRoutingConfigMinScope)
+ "local0",
+ "cell0",
+ multicastRoutingConfigMinScope
+ )
inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
- "cell0", "local0", multicastRoutingConfigSelected)
+ "cell0",
+ "local0",
+ multicastRoutingConfigSelected
+ )
wifiAgent.connect()
@@ -357,9 +410,15 @@
inOrder.verify(multicastRoutingCoordinatorService)
.applyMulticastRoutingConfig("cell0", "local0", CONFIG_FORWARD_NONE)
inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
- "local0", "wifi0", multicastRoutingConfigMinScope)
+ "local0",
+ "wifi0",
+ multicastRoutingConfigMinScope
+ )
inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
- "wifi0", "local0", multicastRoutingConfigSelected)
+ "wifi0",
+ "local0",
+ multicastRoutingConfigSelected
+ )
localAgent.disconnect()
cellAgent.disconnect()
@@ -370,15 +429,19 @@
fun testMulticastRoutingConfig_UpstreamSelectorCellToWifi() {
deps.setBuildSdk(VERSION_V)
val cb = TestableNetworkCallback()
- cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities()
+ cm.registerNetworkCallback(
+ NetworkRequest.Builder().clearCapabilities()
.addCapability(NET_CAPABILITY_LOCAL_NETWORK)
- .build(), cb)
+ .build(),
+ cb
+ )
val inOrder = inOrder(multicastRoutingCoordinatorService)
- val lnc = FromS(LocalNetworkConfig.Builder()
- .setUpstreamSelector(upstreamSelectorCell)
- .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
- .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
- .build()
+ val lnc = FromS(
+ LocalNetworkConfig.Builder()
+ .setUpstreamSelector(upstreamSelectorCell)
+ .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
+ .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
+ .build()
)
val localAgent = createLocalAgent("local0", lnc)
val wifiAgent = createWifiAgent("wifi0")
@@ -393,12 +456,22 @@
}
inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
- "local0", "cell0", multicastRoutingConfigMinScope)
+ "local0",
+ "cell0",
+ multicastRoutingConfigMinScope
+ )
inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
- "cell0", "local0", multicastRoutingConfigSelected)
+ "cell0",
+ "local0",
+ multicastRoutingConfigSelected
+ )
- sendLocalNetworkConfig(localAgent, upstreamSelectorWifi, multicastRoutingConfigMinScope,
- multicastRoutingConfigSelected)
+ sendLocalNetworkConfig(
+ localAgent,
+ upstreamSelectorWifi,
+ multicastRoutingConfigMinScope,
+ multicastRoutingConfigSelected
+ )
cb.expect<LocalInfoChanged>(localAgent.network) {
it.info.upstreamNetwork == wifiAgent.network
}
@@ -409,9 +482,15 @@
inOrder.verify(multicastRoutingCoordinatorService)
.applyMulticastRoutingConfig("cell0", "local0", CONFIG_FORWARD_NONE)
inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
- "local0", "wifi0", multicastRoutingConfigMinScope)
+ "local0",
+ "wifi0",
+ multicastRoutingConfigMinScope
+ )
inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
- "wifi0", "local0", multicastRoutingConfigSelected)
+ "wifi0",
+ "local0",
+ multicastRoutingConfigSelected
+ )
localAgent.disconnect()
cellAgent.disconnect()
@@ -422,15 +501,19 @@
fun testMulticastRoutingConfig_UpstreamSelectorWifiToNull() {
deps.setBuildSdk(VERSION_V)
val cb = TestableNetworkCallback()
- cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities()
+ cm.registerNetworkCallback(
+ NetworkRequest.Builder().clearCapabilities()
.addCapability(NET_CAPABILITY_LOCAL_NETWORK)
- .build(), cb)
+ .build(),
+ cb
+ )
val inOrder = inOrder(multicastRoutingCoordinatorService)
- val lnc = FromS(LocalNetworkConfig.Builder()
- .setUpstreamSelector(upstreamSelectorWifi)
- .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
- .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
- .build()
+ val lnc = FromS(
+ LocalNetworkConfig.Builder()
+ .setUpstreamSelector(upstreamSelectorWifi)
+ .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
+ .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
+ .build()
)
val localAgent = createLocalAgent("local0", lnc)
localAgent.connect()
@@ -442,12 +525,22 @@
}
inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
- "local0", "wifi0", multicastRoutingConfigMinScope)
+ "local0",
+ "wifi0",
+ multicastRoutingConfigMinScope
+ )
inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
- "wifi0", "local0", multicastRoutingConfigSelected)
+ "wifi0",
+ "local0",
+ multicastRoutingConfigSelected
+ )
- sendLocalNetworkConfig(localAgent, null, multicastRoutingConfigMinScope,
- multicastRoutingConfigSelected)
+ sendLocalNetworkConfig(
+ localAgent,
+ null,
+ multicastRoutingConfigMinScope,
+ multicastRoutingConfigSelected
+ )
cb.expect<LocalInfoChanged>(localAgent.network) {
it.info.upstreamNetwork == null
}
@@ -458,9 +551,15 @@
inOrder.verify(multicastRoutingCoordinatorService)
.applyMulticastRoutingConfig("wifi0", "local0", CONFIG_FORWARD_NONE)
inOrder.verify(multicastRoutingCoordinatorService, never()).applyMulticastRoutingConfig(
- eq("local0"), any(), eq(multicastRoutingConfigMinScope))
+ eq("local0"),
+ any(),
+ eq(multicastRoutingConfigMinScope)
+ )
inOrder.verify(multicastRoutingCoordinatorService, never()).applyMulticastRoutingConfig(
- any(), eq("local0"), eq(multicastRoutingConfigSelected))
+ any(),
+ eq("local0"),
+ eq(multicastRoutingConfigSelected)
+ )
localAgent.disconnect()
wifiAgent.disconnect()
@@ -482,23 +581,30 @@
cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
// Set up a local agent that should forward its traffic to the best wifi upstream.
- val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
+ val localAgent = Agent(
+ nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
lp = lp("local0"),
- lnc = FromS(LocalNetworkConfig.Builder()
- .setUpstreamSelector(upstreamSelectorWifi)
- .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
- .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
- .build()),
- score = FromS(NetworkScore.Builder()
- .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
- .build())
+ lnc = FromS(
+ LocalNetworkConfig.Builder()
+ .setUpstreamSelector(upstreamSelectorWifi)
+ .setUpstreamMulticastRoutingConfig(multicastRoutingConfigMinScope)
+ .setDownstreamMulticastRoutingConfig(multicastRoutingConfigSelected)
+ .build()
+ ),
+ score = FromS(
+ NetworkScore.Builder()
+ .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
+ .build()
+ )
)
localAgent.connect()
cb.expectAvailableCallbacks(localAgent.network, validated = false)
- val wifiAgent = Agent(lp = lp("wifi0"),
- nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
+ val wifiAgent = Agent(
+ lp = lp("wifi0"),
+ nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET)
+ )
wifiAgent.connect()
cb.expectAvailableCallbacks(wifiAgent.network, validated = false)
@@ -519,8 +625,10 @@
inOrder.verify(netd).networkDestroy(wifiAgent.network.netId)
val wifiIface2 = if (sameIfaceName) "wifi0" else "wifi1"
- val wifiAgent2 = Agent(lp = lp(wifiIface2),
- nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
+ val wifiAgent2 = Agent(
+ lp = lp(wifiIface2),
+ nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET)
+ )
wifiAgent2.connect()
cb.expectAvailableCallbacks(wifiAgent2.network, validated = false)
@@ -529,9 +637,15 @@
inOrder.verify(netd).ipfwdAddInterfaceForward("local0", wifiIface2)
inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
- "local0", wifiIface2, multicastRoutingConfigMinScope)
+ "local0",
+ wifiIface2,
+ multicastRoutingConfigMinScope
+ )
inOrder.verify(multicastRoutingCoordinatorService).applyMulticastRoutingConfig(
- wifiIface2, "local0", multicastRoutingConfigSelected)
+ wifiIface2,
+ "local0",
+ multicastRoutingConfigSelected
+ )
inOrder.verify(netd, never()).ipfwdRemoveInterfaceForward(any(), any())
inOrder.verify(multicastRoutingCoordinatorService, never())
@@ -547,24 +661,35 @@
cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
// Set up a local agent that should forward its traffic to the best wifi upstream.
- val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
+ val localAgent = Agent(
+ nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
lp = lp("local0"),
- lnc = FromS(LocalNetworkConfig.Builder()
- .setUpstreamSelector(NetworkRequest.Builder()
- .addTransportType(TRANSPORT_WIFI)
- .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
- .build())
- .build()),
- score = FromS(NetworkScore.Builder()
- .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
- .build())
+ lnc = FromS(
+ LocalNetworkConfig.Builder()
+ .setUpstreamSelector(
+ NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .addForbiddenCapability(
+ NET_CAPABILITY_LOCAL_NETWORK
+ )
+ .build()
+ )
+ .build()
+ ),
+ score = FromS(
+ NetworkScore.Builder()
+ .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
+ .build()
+ )
)
localAgent.connect()
cb.expectAvailableCallbacks(localAgent.network, validated = false)
- val wifiAgent = Agent(lp = lp("wifi0"),
- nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
+ val wifiAgent = Agent(
+ lp = lp("wifi0"),
+ nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET)
+ )
wifiAgent.connect()
cb.expectAvailableCallbacks(wifiAgent.network, validated = false)
@@ -587,21 +712,27 @@
deps.setBuildSdk(VERSION_V)
val localCb = TestableNetworkCallback()
- cm.requestNetwork(NetworkRequest.Builder().clearCapabilities()
- .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
- .build(),
- localCb)
+ cm.requestNetwork(
+ NetworkRequest.Builder().clearCapabilities()
+ .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+ .build(),
+ localCb
+ )
val cb = TestableNetworkCallback()
cm.registerNetworkCallback(NetworkRequest.Builder().clearCapabilities().build(), cb)
val localNc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK)
- val lnc = FromS(LocalNetworkConfig.Builder()
- .setUpstreamSelector(NetworkRequest.Builder()
- .addTransportType(TRANSPORT_WIFI)
- .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
- .build())
- .build())
+ val lnc = FromS(
+ LocalNetworkConfig.Builder()
+ .setUpstreamSelector(
+ NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
+ .build()
+ )
+ .build()
+ )
val localScore = FromS(NetworkScore.Builder().build())
// Set up a local agent that should forward its traffic to the best wifi upstream.
@@ -628,10 +759,16 @@
val localAgent2 = Agent(nc = localNc, lp = lp("local0"), lnc = lnc, score = localScore)
localAgent2.connect()
- localCb.expectAvailableCallbacks(localAgent2.network,
- validated = false, upstream = wifiAgent.network)
- cb.expectAvailableCallbacks(localAgent2.network,
- validated = false, upstream = wifiAgent.network)
+ localCb.expectAvailableCallbacks(
+ localAgent2.network,
+ validated = false,
+ upstream = wifiAgent.network
+ )
+ cb.expectAvailableCallbacks(
+ localAgent2.network,
+ validated = false,
+ upstream = wifiAgent.network
+ )
cb.expect<Lost> { it.network == localAgent.network }
}
@@ -648,24 +785,36 @@
// Unregister wifi pending replacement, then set up a local agent that would have
// this network as its upstream.
wifiAgent.unregisterAfterReplacement(LONG_TIMEOUT_MS)
- val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
+ val localAgent = Agent(
+ nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
lp = lp("local0"),
- lnc = FromS(LocalNetworkConfig.Builder()
- .setUpstreamSelector(NetworkRequest.Builder()
- .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
- .addTransportType(TRANSPORT_WIFI)
- .build())
- .build()),
- score = FromS(NetworkScore.Builder()
- .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
- .build())
+ lnc = FromS(
+ LocalNetworkConfig.Builder()
+ .setUpstreamSelector(
+ NetworkRequest.Builder()
+ .addForbiddenCapability(
+ NET_CAPABILITY_LOCAL_NETWORK
+ )
+ .addTransportType(TRANSPORT_WIFI)
+ .build()
+ )
+ .build()
+ ),
+ score = FromS(
+ NetworkScore.Builder()
+ .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
+ .build()
+ )
)
// Connect the local agent. The zombie wifi is its upstream, but the stack doesn't
// tell netd to add the forward since the wifi0 interface has gone.
localAgent.connect()
- cb.expectAvailableCallbacks(localAgent.network,
- validated = false, upstream = wifiAgent.network)
+ cb.expectAvailableCallbacks(
+ localAgent.network,
+ validated = false,
+ upstream = wifiAgent.network
+ )
verify(netd, never()).ipfwdAddInterfaceForward("local0", "wifi0")
@@ -679,33 +828,51 @@
fun testForwardingRules() {
deps.setBuildSdk(VERSION_V)
// Set up a local agent that should forward its traffic to the best DUN upstream.
- val lnc = FromS(LocalNetworkConfig.Builder()
- .setUpstreamSelector(NetworkRequest.Builder()
- .addCapability(NET_CAPABILITY_DUN)
- .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
- .build())
- .build())
- val localAgent = Agent(nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
+ val lnc = FromS(
+ LocalNetworkConfig.Builder()
+ .setUpstreamSelector(
+ NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_DUN)
+ .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
+ .build()
+ )
+ .build()
+ )
+ val localAgent = Agent(
+ nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_LOCAL_NETWORK),
lp = lp("local0"),
lnc = lnc,
- score = FromS(NetworkScore.Builder()
- .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
- .build())
+ score = FromS(
+ NetworkScore.Builder()
+ .setKeepConnectedReason(KEEP_CONNECTED_LOCAL_NETWORK)
+ .build()
+ )
)
localAgent.connect()
- val wifiAgent = Agent(score = keepScore(), lp = lp("wifi0"),
- nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET))
- val cellAgentDun = Agent(score = keepScore(), lp = lp("cell0"),
- nc = nc(TRANSPORT_CELLULAR, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN))
- val wifiAgentDun = Agent(score = keepScore(), lp = lp("wifi1"),
- nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN))
+ val wifiAgent = Agent(
+ score = keepScore(),
+ lp = lp("wifi0"),
+ nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET)
+ )
+ val cellAgentDun = Agent(
+ score = keepScore(),
+ lp = lp("cell0"),
+ nc = nc(TRANSPORT_CELLULAR, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN)
+ )
+ val wifiAgentDun = Agent(
+ score = keepScore(),
+ lp = lp("wifi1"),
+ nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN)
+ )
val cb = TestableNetworkCallback()
- cm.registerNetworkCallback(NetworkRequest.Builder()
- .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
- .build(),
- cb)
+ cm.registerNetworkCallback(
+ NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+ .build(),
+ cb
+ )
cb.expectAvailableCallbacks(localAgent.network, validated = false)
val inOrder = inOrder(netd)
@@ -750,8 +917,11 @@
inOrder.verify(netd).ipfwdDisableForwarding(any())
cb.expect<LocalInfoChanged>(localAgent.network) { it.info.upstreamNetwork == null }
- val wifiAgentDun2 = Agent(score = keepScore(), lp = lp("wifi2"),
- nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN))
+ val wifiAgentDun2 = Agent(
+ score = keepScore(),
+ lp = lp("wifi2"),
+ nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN)
+ )
wifiAgentDun2.connect()
inOrder.verify(netd).ipfwdEnableForwarding(any())
inOrder.verify(netd).ipfwdAddInterfaceForward("local0", "wifi2")
@@ -764,8 +934,11 @@
inOrder.verify(netd).ipfwdDisableForwarding(any())
cb.expect<LocalInfoChanged>(localAgent.network) { it.info.upstreamNetwork == null }
- val wifiAgentDun3 = Agent(score = keepScore(), lp = lp("wifi3"),
- nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN))
+ val wifiAgentDun3 = Agent(
+ score = keepScore(),
+ lp = lp("wifi3"),
+ nc = nc(TRANSPORT_WIFI, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN)
+ )
wifiAgentDun3.connect()
inOrder.verify(netd).ipfwdEnableForwarding(any())
inOrder.verify(netd).ipfwdAddInterfaceForward("local0", "wifi3")
@@ -800,8 +973,11 @@
cm.registerNetworkCallback(nr, listenCb)
val upstream = if (haveUpstream) {
- Agent(score = keepScore(), lp = lp("wifi0"),
- nc = nc(TRANSPORT_WIFI)).also { it.connect() }
+ Agent(
+ score = keepScore(),
+ lp = lp("wifi0"),
+ nc = nc(TRANSPORT_WIFI)
+ ).also { it.connect() }
} else {
null
}
@@ -809,23 +985,32 @@
// Set up a local agent.
val lnc = FromS(LocalNetworkConfig.Builder().apply {
if (haveUpstream) {
- setUpstreamSelector(NetworkRequest.Builder()
- .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
- .addTransportType(TRANSPORT_WIFI)
- .build())
+ setUpstreamSelector(
+ NetworkRequest.Builder()
+ .addForbiddenCapability(NET_CAPABILITY_LOCAL_NETWORK)
+ .addTransportType(TRANSPORT_WIFI)
+ .build()
+ )
}
}.build())
- val localAgent = Agent(nc = nc(TRANSPORT_THREAD, NET_CAPABILITY_LOCAL_NETWORK),
+ val localAgent = Agent(
+ nc = nc(TRANSPORT_THREAD, NET_CAPABILITY_LOCAL_NETWORK),
lp = lp("local0"),
lnc = lnc,
score = FromS(NetworkScore.Builder().build())
)
localAgent.connect()
- requestCb.expectAvailableCallbacks(localAgent.network,
- validated = false, upstream = upstream?.network)
- listenCb.expectAvailableCallbacks(localAgent.network,
- validated = false, upstream = upstream?.network)
+ requestCb.expectAvailableCallbacks(
+ localAgent.network,
+ validated = false,
+ upstream = upstream?.network
+ )
+ listenCb.expectAvailableCallbacks(
+ localAgent.network,
+ validated = false,
+ upstream = upstream?.network
+ )
cm.unregisterNetworkCallback(requestCb)
@@ -866,12 +1051,14 @@
doTestLocalNetworkRequest(
request,
enableMatchLocalNetwork = false,
- expectCallback = false)
+ expectCallback = false
+ )
// If ENABLE_MATCH_LOCAL_NETWORK is true, request is satisfied by local network
doTestLocalNetworkRequest(
request,
enableMatchLocalNetwork = true,
- expectCallback = true)
+ expectCallback = true
+ )
}
@Test
@@ -880,10 +1067,12 @@
doTestLocalNetworkRequest(
request,
enableMatchLocalNetwork = false,
- expectCallback = true)
+ expectCallback = true
+ )
doTestLocalNetworkRequest(
request,
enableMatchLocalNetwork = true,
- expectCallback = true)
+ expectCallback = true
+ )
}
}
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt
index 9be7d11..6e07ac6 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt
@@ -104,7 +104,8 @@
doNothing().`when`(networkStack).makeNetworkMonitor(
nmNetworkCaptor.capture(),
any() /* name */,
- nmCbCaptor.capture())
+ nmCbCaptor.capture()
+ )
// Create the actual agent. NetworkAgent is abstract, so make an anonymous subclass.
if (deps.isAtLeastS()) {
@@ -157,8 +158,10 @@
// Instead of forcefully adding NOT_SUSPENDED to all agents like older tools did,
// it's better to let the developer manage it as they see fit but help them
// debug if they forget.
- fail("Could not connect the agent. Did you forget to add " +
- "NET_CAPABILITY_NOT_SUSPENDED ?")
+ fail(
+ "Could not connect the agent. Did you forget to add " +
+ "NET_CAPABILITY_NOT_SUSPENDED ?"
+ )
}
fail("Could not connect the agent. Instrumentation failure ?")
}
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt
index a53d430..5e18843 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSTestHelpers.kt
@@ -114,8 +114,10 @@
)
doReturn(true).`when`(pm).hasSystemFeature(argThat { supported.contains(it) })
val myPackageName = realContext.packageName
- val myPackageInfo = realContext.packageManager.getPackageInfo(myPackageName,
- PackageManager.GET_PERMISSIONS)
+ val myPackageInfo = realContext.packageManager.getPackageInfo(
+ myPackageName,
+ PackageManager.GET_PERMISSIONS
+ )
// Very high version code so that the checks for the module version will always
// say that it is recent enough. This is the most sensible default, but if some
// test needs to test with different version codes they can re-mock this with a
@@ -123,7 +125,10 @@
myPackageInfo.longVersionCode = 9999999L
doReturn(arrayOf(myPackageName)).`when`(pm).getPackagesForUid(Binder.getCallingUid())
doReturn(myPackageInfo).`when`(pm).getPackageInfoAsUser(
- eq(myPackageName), anyInt(), eq(UserHandle.getCallingUserId()))
+ eq(myPackageName),
+ anyInt(),
+ eq(UserHandle.getCallingUserId())
+ )
doReturn(listOf(myPackageInfo)).`when`(pm)
.getInstalledPackagesAsUser(eq(PackageManager.GET_PERMISSIONS), anyInt())
}
@@ -144,12 +149,19 @@
handler as Handler
val delayMs = ((date as Long) - SystemClock.elapsedRealtime()).coerceAtLeast(0)
if (delayMs > UNREASONABLY_LONG_ALARM_WAIT_MS) {
- fail("Attempting to send msg more than $UNREASONABLY_LONG_ALARM_WAIT_MS" +
- "ms into the future : $delayMs")
+ fail(
+ "Attempting to send msg more than $UNREASONABLY_LONG_ALARM_WAIT_MS" +
+ "ms into the future : $delayMs"
+ )
}
alrmHdlr.postDelayed({ handler.post(wakeupMsg::onAlarm) }, wakeupMsg, delayMs)
- }.`when`(am).setExact(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), anyLong(), anyString(),
- any<WakeupMessage>(), any())
+ }.`when`(am).setExact(
+ eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+ anyLong(),
+ anyString(),
+ any<WakeupMessage>(),
+ any()
+ )
doAnswer {
alrmHdlr.removeCallbacksAndMessages(it.getArgument<WakeupMessage>(0))
}.`when`(am).cancel(any<WakeupMessage>())
@@ -193,14 +205,20 @@
private val TEST_LINGER_DELAY_MS = 400
private val TEST_NASCENT_DELAY_MS = 300
-internal fun makeConnectivityService(context: Context, netd: INetd, deps: Dependencies,
- mPermDeps: PermissionMonitor.Dependencies) =
+internal fun makeConnectivityService(
+ context: Context,
+ netd: INetd,
+ deps: Dependencies,
+ mPermDeps: PermissionMonitor.Dependencies
+) =
ConnectivityService(
context,
mock<IDnsResolver>(),
mock<IpConnectivityLog>(),
netd,
- deps, mPermDeps).also {
+ deps,
+ mPermDeps
+ ).also {
it.mLingerDelayMs = TEST_LINGER_DELAY_MS
it.mNascentDelayMs = TEST_NASCENT_DELAY_MS
}
diff --git a/thread/docs/build-an-android-border-router.md b/thread/docs/build-an-android-border-router.md
index f90a23b..2687e26 100644
--- a/thread/docs/build-an-android-border-router.md
+++ b/thread/docs/build-an-android-border-router.md
@@ -380,10 +380,12 @@
[config_thread.xml](https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Connectivity/service/ServiceConnectivityResources/res/values/config_thread.xml)
for the full list.
-Typically, you must change the `config_thread_vendor_name`,
-`config_thread_vendor_oui` and `config_thread_model_name` to your vendor or
-product values. Those values will be included in the `_meshcop._udp` mDNS
-service which is always advertised by a Thread Border Router.
+Typically, you must set `config_thread_border_router_default_enabled` to `true`
+to enable your device as a Thread Border Router, and change the
+`config_thread_vendor_name`, `config_thread_vendor_oui` and
+`config_thread_model_name` to your vendor or product values. Those values will
+be included in the `_meshcop._udp` mDNS service which is always advertised by a
+Thread Border Router.
To add the overlay, you need to create a new `ConnectivityOverlayOrange`
runtime_resource_overlay target for your Orange device. Create a new
@@ -436,6 +438,7 @@
```
- `config_thread.xml`:
```
+ <bool name="config_thread_border_router_default_enabled">true</bool>
<string translatable="false" name="config_thread_vendor_name">Banana Inc.</string>
<string translatable="false" name="config_thread_vendor_oui">AC:DE:48</string>
<string translatable="false" name="config_thread_model_name">Orange</string>
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
index 7063357..d859fb2 100644
--- a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
@@ -628,12 +628,15 @@
boolean srpServerWaitEnabled = mResources.get().getBoolean(srpServerConfig);
int autoJoinConfig = R.bool.config_thread_border_router_auto_join_enabled;
boolean autoJoinEnabled = mResources.get().getBoolean(autoJoinConfig);
+ boolean countryCodeEnabled =
+ mResources.get().getBoolean(R.bool.config_thread_country_code_enabled);
return new OtDaemonConfiguration.Builder()
.setBorderRouterEnabled(threadConfig.isBorderRouterEnabled())
.setNat64Enabled(threadConfig.isNat64Enabled())
.setDhcpv6PdEnabled(threadConfig.isDhcpv6PdEnabled())
.setSrpServerWaitForBorderRoutingEnabled(srpServerWaitEnabled)
.setBorderRouterAutoJoinEnabled(autoJoinEnabled)
+ .setCountryCodeEnabled(countryCodeEnabled)
.build();
}
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkCountryCode.java b/thread/service/java/com/android/server/thread/ThreadNetworkCountryCode.java
index a96d06e..16196fa 100644
--- a/thread/service/java/com/android/server/thread/ThreadNetworkCountryCode.java
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkCountryCode.java
@@ -17,6 +17,7 @@
package com.android.server.thread;
import static android.net.thread.ThreadNetworkException.ERROR_UNSUPPORTED_FEATURE;
+
import static com.android.server.thread.ThreadPersistentSettings.KEY_COUNTRY_CODE;
import android.annotation.Nullable;
@@ -223,6 +224,10 @@
.getBoolean(R.bool.config_thread_location_use_for_country_code_enabled);
}
+ private boolean isCountryCodeEnabled() {
+ return mResources.get().getBoolean(R.bool.config_thread_country_code_enabled);
+ }
+
public ThreadNetworkCountryCode(
LocationManager locationManager,
ThreadNetworkControllerService threadNetworkControllerService,
@@ -270,6 +275,11 @@
/** Sets up this country code module to listen to location country code changes. */
public synchronized void initialize() {
+ if (!isCountryCodeEnabled()) {
+ LOG.i("Thread country code is disabled");
+ return;
+ }
+
registerGeocoderCountryCodeCallback();
registerWifiCountryCodeCallback();
registerTelephonyCountryCodeCallback();
@@ -654,6 +664,7 @@
/** Dumps the current state of this ThreadNetworkCountryCode object. */
public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("---- Dump of ThreadNetworkCountryCode begin ----");
+ pw.println("isCountryCodeEnabled : " + isCountryCodeEnabled());
pw.println("mIsCpSettingCountryCodeSupported: " + mIsCpSettingCountryCodeSupported);
pw.println("mOverrideCountryCodeInfo : " + mOverrideCountryCodeInfo);
pw.println("mTelephonyCountryCodeSlotInfoMap: " + mTelephonyCountryCodeSlotInfoMap);
diff --git a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
index 95ebda5..63d6130 100644
--- a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
+++ b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
@@ -261,6 +261,7 @@
.thenReturn(TEST_MODEL_NAME);
when(mResources.getStringArray(eq(R.array.config_thread_mdns_vendor_specific_txts)))
.thenReturn(new String[] {});
+ when(mResources.getBoolean(eq(R.bool.config_thread_country_code_enabled))).thenReturn(true);
final AtomicFile storageFile = new AtomicFile(tempFolder.newFile("thread_settings.xml"));
mPersistentSettings = new ThreadPersistentSettings(storageFile, mConnectivityResources);
diff --git a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkCountryCodeTest.java b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkCountryCodeTest.java
index 6eb9b50..1a6b3cc 100644
--- a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkCountryCodeTest.java
+++ b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkCountryCodeTest.java
@@ -206,6 +206,21 @@
}
@Test
+ public void initialize_countryCodeDisabled_defaultCountryCodeIsUsed() {
+ when(mResources.getBoolean(R.bool.config_thread_country_code_enabled)).thenReturn(false);
+
+ mThreadNetworkCountryCode.initialize();
+
+ verifyNoMoreInteractions(mWifiManager);
+ verifyNoMoreInteractions(mTelephonyManager);
+ verifyNoMoreInteractions(mSubscriptionManager);
+ verifyNoMoreInteractions(mGeocoder);
+ verifyNoMoreInteractions(mLocationManager);
+
+ assertThat(mThreadNetworkCountryCode.getCountryCode()).isEqualTo(DEFAULT_COUNTRY_CODE);
+ }
+
+ @Test
public void initialize_locationUseIsDisabled_locationFunctionIsNotCalled() {
when(mResources.getBoolean(R.bool.config_thread_location_use_for_country_code_enabled))
.thenReturn(false);
@@ -507,6 +522,7 @@
mThreadNetworkCountryCode.dump(new FileDescriptor(), printWriter, null);
String outputString = stringWriter.toString();
+ assertThat(outputString).contains("isCountryCodeEnabled");
assertThat(outputString).contains("mIsCpSettingCountryCodeSupported");
assertThat(outputString).contains("mOverrideCountryCodeInfo");
assertThat(outputString).contains("mTelephonyCountryCodeSlotInfoMap");