Merge "Make sure tests know to handle both early creation true and false" into main
diff --git a/Tethering/src/com/android/networkstack/tethering/TetheringService.java b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
index b92cf69..737041e 100644
--- a/Tethering/src/com/android/networkstack/tethering/TetheringService.java
+++ b/Tethering/src/com/android/networkstack/tethering/TetheringService.java
@@ -146,7 +146,7 @@
mTethering.setUsbTethering(enable, listener);
}
- private boolean isRequestAllowedForDeviceOwner(@NonNull TetheringRequest request) {
+ private boolean isRequestAllowedForDOOrCarrierApp(@NonNull TetheringRequest request) {
return request.getTetheringType() == TETHERING_WIFI
&& request.getSoftApConfiguration() != null;
}
@@ -159,10 +159,10 @@
request.setPackageName(callerPkg);
boolean onlyAllowPrivileged = request.isExemptFromEntitlementCheck()
|| request.getInterfaceName() != null;
- boolean isDeviceOwnerAllowed = mTethering.isTetheringWithSoftApConfigEnabled()
- && isRequestAllowedForDeviceOwner(request);
+ boolean isDOOrCarrierAppAllowed = mTethering.isTetheringWithSoftApConfigEnabled()
+ && isRequestAllowedForDOOrCarrierApp(request);
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag, onlyAllowPrivileged,
- isDeviceOwnerAllowed, listener)) {
+ isDOOrCarrierAppAllowed, listener)) {
return;
}
mTethering.startTethering(request, callerPkg, listener);
@@ -191,10 +191,10 @@
if (listener == null) return;
request.setUid(getBinderCallingUid());
request.setPackageName(callerPkg);
- boolean isDeviceOwnerAllowed = mTethering.isTetheringWithSoftApConfigEnabled()
- && isRequestAllowedForDeviceOwner(request);
+ boolean isDOOrCarrierAppAllowed = mTethering.isTetheringWithSoftApConfigEnabled()
+ && isRequestAllowedForDOOrCarrierApp(request);
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
- false /* onlyAllowPrivileged */, isDeviceOwnerAllowed, listener)) {
+ false /* onlyAllowPrivileged */, isDOOrCarrierAppAllowed, listener)) {
return;
}
// Note: Whether tethering is actually stopped or not will depend on whether the request
@@ -274,9 +274,9 @@
@Override
public void isTetheringSupported(String callerPkg, String callingAttributionTag,
IIntResultListener listener) {
- boolean isDeviceOwnerAppAllowed = mTethering.isTetheringWithSoftApConfigEnabled();
+ boolean isDOOrCarrierAppAllowed = mTethering.isTetheringWithSoftApConfigEnabled();
if (checkAndNotifyCommonError(callerPkg, callingAttributionTag,
- false /* onlyAllowPrivileged */, isDeviceOwnerAppAllowed, listener)) {
+ false /* onlyAllowPrivileged */, isDOOrCarrierAppAllowed, listener)) {
return;
}
try {
@@ -304,7 +304,7 @@
private boolean checkAndNotifyCommonError(final String callerPkg,
final String callingAttributionTag, final boolean onlyAllowPrivileged,
- final boolean isDeviceOwnerAppAllowed, final IIntResultListener listener) {
+ final boolean isDOOrCarrierAppAllowed, final IIntResultListener listener) {
try {
final int uid = getBinderCallingUid();
if (!checkPackageNameMatchesUid(uid, callerPkg)) {
@@ -313,7 +313,7 @@
return true;
}
if (!hasTetherChangePermission(uid, callerPkg, callingAttributionTag,
- onlyAllowPrivileged, isDeviceOwnerAppAllowed)) {
+ onlyAllowPrivileged, isDOOrCarrierAppAllowed)) {
listener.onResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
return true;
}
@@ -347,14 +347,18 @@
private boolean hasTetherChangePermission(final int uid, final String callerPkg,
final String callingAttributionTag, final boolean onlyAllowPrivileged,
- final boolean isDeviceOwnerAppAllowed) {
+ final boolean isDOOrCarrierAppAllowed) {
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)) {
+ // Allow DO and carrier-privileged apps to change tethering even if they don't have
+ // TETHER_PRIVILEGED.
+ // TODO: Stop tethering if the app loses DO status or carrier-privileges.
+ if (isDOOrCarrierAppAllowed
+ && (mService.isDeviceOwner(uid, callerPkg)
+ || mService.isCarrierPrivileged(callerPkg))) {
return true;
}
@@ -436,6 +440,14 @@
}
/**
+ * Wrapper for {@link TetheringPermissionsUtils#isCarrierPrivileged(String)}, used for mocks.
+ */
+ @VisibleForTesting
+ boolean isCarrierPrivileged(final String callerPkg) {
+ return mTetheringPermissionsUtils.isCarrierPrivileged(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
index 944e861..603fa9c 100644
--- a/Tethering/src/com/android/networkstack/tethering/util/TetheringPermissionsUtils.java
+++ b/Tethering/src/com/android/networkstack/tethering/util/TetheringPermissionsUtils.java
@@ -19,7 +19,9 @@
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.UserHandle;
+import android.telephony.TelephonyManager;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -68,4 +70,20 @@
}
return devicePolicyManager;
}
+
+ /**
+ * Checks if the package name has carrier privileges.
+ */
+ public boolean isCarrierPrivileged(@NonNull final String packageName) {
+ TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
+ if (telephonyManager == null) return false;
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName)
+ == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
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 a8bd221..01d7198 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/MockTetheringService.java
@@ -37,6 +37,7 @@
private final ArrayMap<String, Integer> mMockedPermissions = new ArrayMap<>();
private final ArrayMap<String, Integer> mMockedPackageUids = new ArrayMap<>();
private final Set<String> mMockedDeviceOwnerPackages = new ArraySet<>();
+ private final Set<String> mMockedCarrierPrivilegedPackages = new ArraySet<>();
private int mMockCallingUid;
@Override
@@ -83,6 +84,11 @@
return mMockedDeviceOwnerPackages.contains(callerPkg);
}
+ @Override
+ boolean isCarrierPrivileged(final String callerPkg) {
+ return mMockedCarrierPrivilegedPackages.contains(callerPkg);
+ }
+
public Tethering getTethering() {
return mTethering;
}
@@ -141,5 +147,19 @@
public void removeDeviceOwnerPackage(final String packageName) {
mMockedDeviceOwnerPackages.remove(packageName);
}
+
+ /**
+ * Add a mocked carrier privileges package
+ */
+ public void addCarrierPrivilegedPackage(final String packageName) {
+ mMockedCarrierPrivilegedPackages.add(packageName);
+ }
+
+ /**
+ * Remove a mocked carrier privileges package
+ */
+ public void removeCarrierPrivilegedPackage(final String packageName) {
+ mMockedCarrierPrivilegedPackages.remove(packageName);
+ }
}
}
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 87163ef..b58fa14 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringServiceTest.java
@@ -90,6 +90,15 @@
private static final int TEST_CALLER_UID = 1234;
private static final String TEST_ATTRIBUTION_TAG = null;
private static final String TEST_WRONG_PACKAGE = "wrong.package";
+ private static final int NO_RESULT = -1;
+ private static final TetheringRequest USB_REQUEST =
+ new TetheringRequest.Builder(TETHERING_USB).build();
+ private static final TetheringRequest WIFI_REQUEST_NO_CONFIG =
+ new TetheringRequest.Builder(TETHERING_WIFI).build();
+ private static final TetheringRequest WIFI_REQUEST_WITH_CONFIG =
+ new TetheringRequest.Builder(TETHERING_WIFI)
+ .setSoftApConfiguration(new SoftApConfiguration.Builder().build())
+ .build();
@Mock private ITetheringEventCallback mITetheringEventCallback;
@Rule public ServiceTestRule mServiceTestRule;
private Tethering mTethering;
@@ -100,7 +109,7 @@
@Mock private AppOpsManager mAppOps;
private class TestTetheringResult extends IIntResultListener.Stub {
- private int mResult = -1; // Default value that does not match any result code.
+ private int mResult = NO_RESULT;
@Override
public void onResult(final int resultCode) {
mResult = resultCode;
@@ -115,7 +124,7 @@
MyResultReceiver(Handler handler) {
super(handler);
}
- private int mResult = -1; // Default value that does not match any result code.
+ private int mResult = NO_RESULT;
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
mResult = resultCode;
@@ -206,6 +215,21 @@
mMockConnector.removeDeviceOwnerPackage(TEST_CALLER_PKG);
}
+ private void runAsCarrierPrivileged(final TestTetheringCall test) throws Exception {
+ mMockConnector.addCarrierPrivilegedPackage(TEST_CALLER_PKG);
+ runTetheringCall(test, true /* isTetheringAllowed */,
+ true /* isTetheringWithSoftApConfigEnabled */, new String[0]);
+ mMockConnector.removeCarrierPrivilegedPackage(TEST_CALLER_PKG);
+ }
+
+ private void runAsCarrierPrivilegedWhenCarrierPrivilegeBypassNotEnabled(
+ final TestTetheringCall test) throws Exception {
+ mMockConnector.addCarrierPrivilegedPackage(TEST_CALLER_PKG);
+ runTetheringCall(test, true /* isTetheringAllowed */,
+ false /* isTetheringWithSoftApConfigEnabled */, new String[0]);
+ mMockConnector.removeCarrierPrivilegedPackage(TEST_CALLER_PKG);
+ }
+
private void runTetheringCall(final TestTetheringCall test, boolean isTetheringAllowed,
boolean isTetheringWithSoftApConfigEnabled, String... permissions) throws Exception {
// Allow the test to run even if ACCESS_NETWORK_STATE was granted at the APK level
@@ -381,125 +405,114 @@
});
}
- private void runStartTethering(final TestTetheringResult result,
- 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(
- eq(new TetheringRequest(request)), eq(TEST_CALLER_PKG), eq(result));
+ private void verifyStartTetheringRequestSucceeds(final TetheringRequest request,
+ final TestTetheringResult result) throws Exception {
+ mTetheringConnector.startTethering(request.getParcel(), TEST_CALLER_PKG,
+ TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering).startTethering(eq(request), eq(TEST_CALLER_PKG), eq(result));
+ reset(mTethering);
+ result.assertResult(NO_RESULT);
+ }
+
+ private void verifyStartTetheringRequestFails(final TetheringRequest request,
+ final TestTetheringResult result, final int resultCode) throws Exception {
+ mTetheringConnector.startTethering(request.getParcel(), TEST_CALLER_PKG,
+ TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering, never()).startTethering(any(), any(), any());
+ reset(mTethering);
+ result.assertResult(resultCode);
+ }
+
+ private void verifyStartTetheringRequestWithWrongPackageFails(final TetheringRequest request,
+ final TestTetheringResult result) throws Exception {
+ mTetheringConnector.startTethering(request.getParcel(), TEST_WRONG_PACKAGE,
+ TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering, never()).startTethering(any(), any(), any());
+ reset(mTethering);
+ result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
}
@Test
public void testStartTethering() throws Exception {
- final TetheringRequestParcel request = new TetheringRequestParcel();
- request.tetheringType = TETHERING_WIFI;
-
runAsNoPermission((result) -> {
- mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
- result);
- 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();
+ verifyStartTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
+ TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
});
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);
- verifyNoMoreInteractionsForTethering();
+ verifyStartTetheringRequestSucceeds(WIFI_REQUEST_NO_CONFIG, result);
});
runAsTetherPrivileged((result) -> {
- runStartTethering(result, request);
- verifyNoMoreInteractionsForTethering();
+ verifyStartTetheringRequestWithWrongPackageFails(WIFI_REQUEST_NO_CONFIG, result);
});
runAsWriteSettings((result) -> {
- mTetheringConnector.startTethering(request, TEST_CALLER_PKG, TEST_ATTRIBUTION_TAG,
- result);
- verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
- result.assertResult(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
- verifyNoMoreInteractionsForTethering();
+ verifyStartTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
+ TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
});
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
- runStartTethering(result, request);
- verify(mTethering, atLeastOnce()).isTetheringWithSoftApConfigEnabled();
- verify(mTethering).isTetherProvisioningRequired();
- verifyNoMoreInteractionsForTethering();
+ verifyStartTetheringRequestSucceeds(WIFI_REQUEST_NO_CONFIG, result);
});
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);
- verifyNoMoreInteractionsForTethering();
+ verifyStartTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
+ TETHER_ERROR_UNSUPPORTED);
+ });
+
+ // Not wifi -> fail
+ runAsDeviceOwner((result) -> {
+ verifyStartTetheringRequestFails(USB_REQUEST, result,
+ TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ });
+
+ // No SoftApConfiguration -> fail
+ runAsDeviceOwner((result) -> {
+ verifyStartTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
+ TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ });
+
+ // With SoftApConfiguration -> success
+ runAsDeviceOwner((result) -> {
+ verifyStartTetheringRequestSucceeds(WIFI_REQUEST_WITH_CONFIG, result);
+ });
+
+ runAsDeviceOwnerWhenDeviceOwnerBypassNotEnabled((result) -> {
+ verifyStartTetheringRequestFails(WIFI_REQUEST_WITH_CONFIG, result,
+ TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ });
+
+ // Not wifi -> fail
+ runAsCarrierPrivileged((result) -> {
+ verifyStartTetheringRequestFails(USB_REQUEST, result,
+ TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ });
+
+ // No SoftApConfiguration -> fail
+ runAsCarrierPrivileged((result) -> {
+ verifyStartTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
+ TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ });
+
+ // With SoftApConfiguration -> success
+ runAsCarrierPrivileged((result) -> {
+ verifyStartTetheringRequestSucceeds(WIFI_REQUEST_WITH_CONFIG, result);
+ });
+
+ runAsCarrierPrivilegedWhenCarrierPrivilegeBypassNotEnabled((result) -> {
+ verifyStartTetheringRequestFails(WIFI_REQUEST_WITH_CONFIG, result,
+ TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
});
}
@Test
public void testStartTetheringWithInterfaceSucceeds() throws Exception {
- final TetheringRequestParcel request = new TetheringRequestParcel();
- request.tetheringType = TETHERING_VIRTUAL;
- request.interfaceName = "avf_tap_fixed";
-
+ final TetheringRequest request = new TetheringRequest.Builder(TETHERING_VIRTUAL)
+ .setInterfaceName("avf_tap_fixed")
+ .build();
runAsNetworkSettings((result) -> {
- runStartTethering(result, request);
+ verifyStartTetheringRequestSucceeds(request, result);
verifyNoMoreInteractionsForTethering();
});
}
@@ -599,84 +612,110 @@
});
}
- private void verifyHasPermissionForStopTetheringRequest(TetheringRequest request,
+ private void verifyStopTetheringRequestSucceeds(final 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);
+ result.assertResult(NO_RESULT);
}
- private void verifyDoesNotHavePermissionForStopTetheringRequest(TetheringRequest request,
- final TestTetheringResult result) throws Exception {
+ private void verifyStopTetheringRequestFails(final TetheringRequest request,
+ final TestTetheringResult result, int resultCode) throws Exception {
mTetheringConnector.stopTetheringRequest(request, TEST_CALLER_PKG,
TEST_ATTRIBUTION_TAG, result);
verify(mTethering, never()).stopTetheringRequest(any(), any());
+ reset(mTethering);
+ result.assertResult(resultCode);
+ }
+
+ private void verifyStopTetheringRequestWithWrongPackageFails(final TetheringRequest request,
+ final TestTetheringResult result) throws Exception {
+ mTetheringConnector.stopTetheringRequest(request, TEST_WRONG_PACKAGE,
+ TEST_ATTRIBUTION_TAG, result);
+ verify(mTethering, never()).stopTetheringRequest(any(), any());
+ reset(mTethering);
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);
+ verifyStopTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
+ TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
});
runAsTetherPrivileged((result) -> {
- verifyHasPermissionForStopTetheringRequest(request, result);
+ verifyStopTetheringRequestSucceeds(WIFI_REQUEST_NO_CONFIG, result);
+ });
+
+ runAsTetherPrivileged((result) -> {
+ verifyStopTetheringRequestWithWrongPackageFails(WIFI_REQUEST_NO_CONFIG, result);
});
runAsWriteSettings((result) -> {
- verifyDoesNotHavePermissionForStopTetheringRequest(request, result);
+ verifyStopTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
+ TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
});
runAsWriteSettingsWhenWriteSettingsAllowed((result) -> {
- verifyHasPermissionForStopTetheringRequest(request, result);
+ // Note: This can't happen in practice since WRITE_SETTINGS is only allowed on V- and
+ // stopTetheringRequest is only allowed on B+, but we test here for completeness.
+ verifyStopTetheringRequestSucceeds(WIFI_REQUEST_NO_CONFIG, result);
});
runAsTetheringDisallowed((result) -> {
- verifyStopTetheringRequestWithTetheringDisallowed(request, result);
+ verifyStopTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
+ TETHER_ERROR_UNSUPPORTED);
});
runAsNetworkSettings((result) -> {
- verifyHasPermissionForStopTetheringRequest(request, result);
+ verifyStopTetheringRequestSucceeds(WIFI_REQUEST_NO_CONFIG, result);
});
// Not wifi -> fail
runAsDeviceOwner((result) -> {
- TetheringRequest notWifi = new TetheringRequest.Builder(TETHERING_USB).build();
- verifyDoesNotHavePermissionForStopTetheringRequest(notWifi, result);
+ verifyStopTetheringRequestFails(USB_REQUEST, result,
+ TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
});
- // No config -> fail
+ // No SoftApConfiguration -> fail
runAsDeviceOwner((result) -> {
- TetheringRequest noConfig = new TetheringRequest.Builder(TETHERING_WIFI).build();
- verifyDoesNotHavePermissionForStopTetheringRequest(noConfig, result);
+ verifyStopTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
+ TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
});
- // With config -> success
- TetheringRequest withConfig = new TetheringRequest.Builder(TETHERING_WIFI)
- .setSoftApConfiguration(new SoftApConfiguration.Builder().build())
- .build();
+ // With SoftApConfiguration -> success
runAsDeviceOwner((result) -> {
- verifyHasPermissionForStopTetheringRequest(withConfig, result);
+ verifyStopTetheringRequestSucceeds(WIFI_REQUEST_WITH_CONFIG, result);
});
runAsDeviceOwnerWhenDeviceOwnerBypassNotEnabled((result) -> {
- verifyDoesNotHavePermissionForStopTetheringRequest(withConfig, result);
+ verifyStopTetheringRequestFails(WIFI_REQUEST_WITH_CONFIG, result,
+ TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ });
+
+ // Not wifi -> fail
+ runAsCarrierPrivileged((result) -> {
+ verifyStopTetheringRequestFails(USB_REQUEST, result,
+ TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ });
+
+ // No SoftApConfiguration -> fail
+ runAsCarrierPrivileged((result) -> {
+ verifyStopTetheringRequestFails(WIFI_REQUEST_NO_CONFIG, result,
+ TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ });
+
+ // With SoftApConfiguration -> success
+ runAsCarrierPrivileged((result) -> {
+ verifyStopTetheringRequestSucceeds(WIFI_REQUEST_WITH_CONFIG, result);
+ });
+
+ runAsCarrierPrivilegedWhenCarrierPrivilegeBypassNotEnabled((result) -> {
+ verifyStopTetheringRequestFails(WIFI_REQUEST_WITH_CONFIG, result,
+ TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
});
}
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
index 57c3eca..2b70e39 100644
--- a/Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringPermissionsUtilsTest.java
+++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/util/TetheringPermissionsUtilsTest.java
@@ -67,4 +67,15 @@
when(mDevicePolicyManager.isDeviceOwnerApp(TEST_PACKAGE)).thenReturn(true);
assertThat(mTetheringPermissionsUtils.isDeviceOwner(TEST_UID, TEST_PACKAGE)).isTrue();
}
+
+ @Test
+ public void testHasCarrierPrivilege() {
+ when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(TEST_PACKAGE))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
+ assertThat(mTetheringPermissionsUtils.isCarrierPrivileged(TEST_PACKAGE)).isFalse();
+
+ when(mTelephonyManager.checkCarrierPrivilegesForPackageAnyPhone(TEST_PACKAGE))
+ .thenReturn(TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS);
+ assertThat(mTetheringPermissionsUtils.isCarrierPrivileged(TEST_PACKAGE)).isTrue();
+ }
}
diff --git a/bpf/loader/NetBpfLoad.cpp b/bpf/loader/NetBpfLoad.cpp
index d70a2c8..63de1a6 100644
--- a/bpf/loader/NetBpfLoad.cpp
+++ b/bpf/loader/NetBpfLoad.cpp
@@ -556,9 +556,9 @@
vector<string> csSymNames;
ret = getSectionSymNames(elfFile, oldName, csSymNames, STT_FUNC);
if (ret || !csSymNames.size()) return ret;
- for (size_t i = 0; i < progDefNames.size(); ++i) {
- if (!progDefNames[i].compare(csSymNames[0] + "_def")) {
- cs_temp.prog_def = pd[i];
+ for (size_t j = 0; j < progDefNames.size(); ++j) {
+ if (!progDefNames[j].compare(csSymNames[0] + "_def")) {
+ cs_temp.prog_def = pd[j];
break;
}
}
@@ -769,7 +769,7 @@
const size_t max_name = 256;
char kvTypeName[max_name];
int64_t keySize, valueSize;
- uint32_t kvId;
+ int32_t kvId;
if (snprintf(kvTypeName, max_name, "____btf_map_%s", mapName) == max_name) {
ALOGE("____btf_map_%s is too long", mapName);
@@ -858,14 +858,16 @@
struct btf *btf = NULL;
auto scopeGuard = base::make_scope_guard([btf] { if (btf) btf__free(btf); });
- if (isAtLeastKernelVersion(4, 18, 0)) {
+ if (isAtLeastKernelVersion(5, 10, 0)) {
+ // Untested on Linux Kernel 5.4, but likely compatible.
// On Linux Kernels older than 4.18 BPF_BTF_LOAD command doesn't exist.
+ // On Linux Kernels older than 5.2 BTF_KIND_VAR and BTF_KIND_DATASEC don'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());
+ btf = btf__new(btfData.data(), btfData.size());
if (btf == NULL) {
ALOGE("btf__new failed, errno: %d", errno);
return -errno;
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index c6b62ee..8355d31 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -360,6 +360,8 @@
mUnderlyingNetworks = null;
mEnterpriseId = 0;
mReservationId = RES_ID_UNSET;
+ // TODO: Change to default disabled when introduce this filtering.
+ mMatchNonThreadLocalNetworks = true;
}
/**
@@ -395,6 +397,7 @@
mUnderlyingNetworks = nc.mUnderlyingNetworks;
mEnterpriseId = nc.mEnterpriseId;
mReservationId = nc.mReservationId;
+ mMatchNonThreadLocalNetworks = nc.mMatchNonThreadLocalNetworks;
}
/**
@@ -2236,7 +2239,8 @@
&& (onlyImmutable || satisfiedBySSID(nc))
&& (onlyImmutable || satisfiedByRequestor(nc))
&& (onlyImmutable || satisfiedBySubscriptionIds(nc)))
- && satisfiedByReservationId(nc);
+ && satisfiedByReservationId(nc)
+ && satisfiedByMatchNonThreadLocalNetworks(nc);
}
/**
@@ -2351,7 +2355,8 @@
&& equalsSubscriptionIds(that)
&& equalsUnderlyingNetworks(that)
&& equalsEnterpriseCapabilitiesId(that)
- && equalsReservationId(that);
+ && equalsReservationId(that)
+ && equalsMatchNonThreadLocalNetworks(that);
}
@Override
@@ -2371,15 +2376,15 @@
+ Objects.hashCode(mAllowedUids) * 41
+ Objects.hashCode(mSSID) * 43
+ Objects.hashCode(mTransportInfo) * 47
- + Objects.hashCode(mPrivateDnsBroken) * 53
+ + Boolean.hashCode(mPrivateDnsBroken) * 53
+ Objects.hashCode(mRequestorUid) * 59
+ Objects.hashCode(mRequestorPackageName) * 61
+ Arrays.hashCode(mAdministratorUids) * 67
+ Objects.hashCode(mSubIds) * 71
+ Objects.hashCode(mUnderlyingNetworks) * 73
+ mEnterpriseId * 79
- + mReservationId * 83;
-
+ + mReservationId * 83
+ + Boolean.hashCode(mMatchNonThreadLocalNetworks) * 89;
}
@Override
@@ -2418,6 +2423,7 @@
dest.writeTypedList(mUnderlyingNetworks);
dest.writeInt(mEnterpriseId & ALL_VALID_ENTERPRISE_IDS);
dest.writeInt(mReservationId);
+ dest.writeBoolean(mMatchNonThreadLocalNetworks);
}
public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -2454,8 +2460,10 @@
netCap.setUnderlyingNetworks(in.createTypedArrayList(Network.CREATOR));
netCap.mEnterpriseId = in.readInt() & ALL_VALID_ENTERPRISE_IDS;
netCap.mReservationId = in.readInt();
+ netCap.mMatchNonThreadLocalNetworks = in.readBoolean();
return netCap;
}
+
@Override
public NetworkCapabilities[] newArray(int size) {
return new NetworkCapabilities[size];
@@ -2561,6 +2569,10 @@
sb.append(" ReservationId: ").append(isReservationOffer ? "*" : mReservationId);
}
+ if (mMatchNonThreadLocalNetworks) {
+ sb.append(" MatchNonThreadLocalNetworks");
+ }
+
sb.append(" UnderlyingNetworks: ");
if (mUnderlyingNetworks != null) {
sb.append("[");
@@ -2945,7 +2957,45 @@
return mReservationId == nc.mReservationId;
}
+ /**
+ * Flag to control whether a NetworkRequest can match non-thread local networks.
+ * @hide
+ */
+ // TODO: Change to default disabled when introduce this filtering.
+ private boolean mMatchNonThreadLocalNetworks = true;
+ /**
+ * Returns the match non-thread local networks flag.
+ *
+ * @hide
+ */
+ public boolean getMatchNonThreadLocalNetworks() {
+ return mMatchNonThreadLocalNetworks;
+ }
+
+ /**
+ * Set the match non-thread local networks flag.
+ * @hide
+ */
+ public void setMatchNonThreadLocalNetworks(boolean enabled) {
+ mMatchNonThreadLocalNetworks = enabled;
+ }
+
+ private boolean equalsMatchNonThreadLocalNetworks(@NonNull NetworkCapabilities nc) {
+ return mMatchNonThreadLocalNetworks == nc.mMatchNonThreadLocalNetworks;
+ }
+
+ // If the flag was set, the NetworkRequest can match all local networks.
+ // Otherwise, it can only see local networks created by Thread.
+ @SuppressWarnings("FlaggedApi")
+ private boolean satisfiedByMatchNonThreadLocalNetworks(@NonNull NetworkCapabilities nc) {
+ // If the network is not a local network, out of scope.
+ if (!nc.hasCapability(NET_CAPABILITY_LOCAL_NETWORK)) return true;
+ // If there is no restriction on matching non-thread local networks, return.
+ if (mMatchNonThreadLocalNetworks) return true;
+
+ return nc.hasTransport(TRANSPORT_THREAD);
+ }
/**
* Returns a bitmask of all the applicable redactions (based on the permissions held by the
diff --git a/framework/src/android/net/connectivity/ConnectivityCompatChanges.java b/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
index 2261c69..3b2520e 100644
--- a/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
+++ b/framework/src/android/net/connectivity/ConnectivityCompatChanges.java
@@ -139,13 +139,13 @@
/**
* Restrict local network access.
- *
* Apps targeting a release after V will require permissions to access the local network.
*
+ * ToDo: Update the target SDK version once it's finalized.
* @hide
*/
@ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT)
+ @EnabledAfter(targetSdkVersion = 36)
public static final long RESTRICT_LOCAL_NETWORK = 365139289L;
private ConnectivityCompatChanges() {
diff --git a/service-t/src/com/android/server/net/NetworkStatsFactory.java b/service-t/src/com/android/server/net/NetworkStatsFactory.java
index 5ff708d..c5a69c0 100644
--- a/service-t/src/com/android/server/net/NetworkStatsFactory.java
+++ b/service-t/src/com/android/server/net/NetworkStatsFactory.java
@@ -19,6 +19,7 @@
import static android.net.NetworkStats.INTERFACES_ALL;
import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkStats.UID_ALL;
+import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
import android.annotation.NonNull;
import android.content.Context;
@@ -26,15 +27,26 @@
import android.net.UnderlyingNetworkInfo;
import android.os.ServiceSpecificException;
import android.os.SystemClock;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.net.module.util.DeviceConfigUtils;
import com.android.server.BpfNetMaps;
import com.android.server.connectivity.InterfaceTracker;
import java.io.IOException;
import java.net.ProtocolException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
@@ -65,6 +77,18 @@
/** Set containing info about active VPNs and their underlying networks. */
private volatile UnderlyingNetworkInfo[] mUnderlyingNetworkInfos = new UnderlyingNetworkInfo[0];
+ static final String CONFIG_PER_UID_TAG_THROTTLING = "per_uid_tag_throttling";
+ static final String CONFIG_PER_UID_TAG_THROTTLING_THRESHOLD =
+ "per_uid_tag_throttling_threshold";
+ private static final int DEFAULT_TAGS_PER_UID_THRESHOLD = 1000;
+ private static final int DUMP_TAGS_PER_UID_COUNT = 20;
+ private final boolean mSupportPerUidTagThrottling;
+ private final int mPerUidTagThrottlingThreshold;
+
+ // Map for set of distinct tags per uid. Used for tag count limiting.
+ @GuardedBy("mPersistentDataLock")
+ private final SparseArray<SparseBooleanArray> mUidTagSets = new SparseArray<>();
+
// A persistent snapshot of cumulative stats since device start
@GuardedBy("mPersistentDataLock")
private NetworkStats mPersistSnapshot;
@@ -110,6 +134,26 @@
public BpfNetMaps createBpfNetMaps(@NonNull Context ctx) {
return new BpfNetMaps(ctx, new InterfaceTracker(ctx));
}
+
+ /**
+ * Check whether one specific feature is not disabled.
+ * @param name Flag name of the experiment in the tethering namespace.
+ * @see DeviceConfigUtils#isTetheringFeatureNotChickenedOut(Context, String)
+ */
+ public boolean isFeatureNotChickenedOut(@NonNull Context context, @NonNull String name) {
+ return DeviceConfigUtils.isTetheringFeatureNotChickenedOut(context, name);
+ }
+
+ /**
+ * Wrapper method for DeviceConfigUtils#getDeviceConfigPropertyInt for test injections.
+ *
+ * See {@link DeviceConfigUtils#getDeviceConfigPropertyInt(String, String, int)}
+ * for more detailed information.
+ */
+ public int getDeviceConfigPropertyInt(@NonNull String name, int defaultValue) {
+ return DeviceConfigUtils.getDeviceConfigPropertyInt(
+ NAMESPACE_TETHERING, name, defaultValue);
+ }
}
/**
@@ -162,6 +206,10 @@
}
mContext = ctx;
mDeps = deps;
+ mSupportPerUidTagThrottling = mDeps.isFeatureNotChickenedOut(
+ ctx, CONFIG_PER_UID_TAG_THROTTLING);
+ mPerUidTagThrottlingThreshold = mDeps.getDeviceConfigPropertyInt(
+ CONFIG_PER_UID_TAG_THROTTLING_THRESHOLD, DEFAULT_TAGS_PER_UID_THRESHOLD);
}
/**
@@ -210,10 +258,13 @@
requestSwapActiveStatsMapLocked();
// Stats are always read from the inactive map, so they must be read after the
// swap
- final NetworkStats stats = mDeps.getNetworkStatsDetail();
+ final NetworkStats diff = mDeps.getNetworkStatsDetail();
+ // Filter based on UID tag set before merging.
+ final NetworkStats filteredDiff = mSupportPerUidTagThrottling
+ ? filterStatsByUidTagSets(diff) : diff;
// BPF stats are incremental; fold into mPersistSnapshot.
- mPersistSnapshot.setElapsedRealtime(stats.getElapsedRealtime());
- mPersistSnapshot.combineAllValues(stats);
+ mPersistSnapshot.setElapsedRealtime(diff.getElapsedRealtime());
+ mPersistSnapshot.combineAllValues(filteredDiff);
NetworkStats adjustedStats = adjustForTunAnd464Xlat(mPersistSnapshot, prev, vpnArray);
@@ -224,6 +275,41 @@
}
@GuardedBy("mPersistentDataLock")
+ private NetworkStats filterStatsByUidTagSets(NetworkStats stats) {
+ final NetworkStats filteredStats =
+ new NetworkStats(stats.getElapsedRealtime(), stats.size());
+
+ final NetworkStats.Entry entry = new NetworkStats.Entry();
+ final Set<Integer> tooManyTagsUidSet = new ArraySet<>();
+ for (int i = 0; i < stats.size(); i++) {
+ stats.getValues(i, entry);
+ final int uid = entry.uid;
+ final int tag = entry.tag;
+
+ if (tag == NetworkStats.TAG_NONE) {
+ filteredStats.combineValues(entry);
+ continue;
+ }
+
+ SparseBooleanArray tagSet = mUidTagSets.get(uid);
+ if (tagSet == null) {
+ tagSet = new SparseBooleanArray();
+ }
+ if (tagSet.size() < mPerUidTagThrottlingThreshold || tagSet.get(tag)) {
+ filteredStats.combineValues(entry);
+ tagSet.put(tag, true);
+ mUidTagSets.put(uid, tagSet);
+ } else {
+ tooManyTagsUidSet.add(uid);
+ }
+ }
+ if (tooManyTagsUidSet.size() > 0) {
+ Log.wtf(TAG, "Too many tags detected for uids: " + tooManyTagsUidSet);
+ }
+ return filteredStats;
+ }
+
+ @GuardedBy("mPersistentDataLock")
private NetworkStats adjustForTunAnd464Xlat(NetworkStats uidDetailStats,
NetworkStats previousStats, UnderlyingNetworkInfo[] vpnArray) {
// Calculate delta from last snapshot
@@ -307,4 +393,34 @@
pe.initCause(cause);
return pe;
}
+
+ /**
+ * Dump the contents of NetworkStatsFactory.
+ */
+ public void dump(IndentingPrintWriter pw) {
+ dumpUidTagSets(pw);
+ }
+
+ private void dumpUidTagSets(IndentingPrintWriter pw) {
+ pw.println("Top distinct tag counts in UidTagSets:");
+ pw.increaseIndent();
+ final List<Pair<Integer, Integer>> countForUidList = new ArrayList<>();
+ synchronized (mPersistentDataLock) {
+ for (int i = 0; i < mUidTagSets.size(); i++) {
+ final Pair<Integer, Integer> countForUid =
+ new Pair<>(mUidTagSets.keyAt(i), mUidTagSets.valueAt(i).size());
+ countForUidList.add(countForUid);
+ }
+ }
+ Collections.sort(countForUidList,
+ (entry1, entry2) -> Integer.compare(entry2.second, entry1.second));
+ final int dumpSize = Math.min(countForUidList.size(), DUMP_TAGS_PER_UID_COUNT);
+ for (int j = 0; j < dumpSize; j++) {
+ final Pair<Integer, Integer> entry = countForUidList.get(j);
+ pw.print(entry.first);
+ pw.print("=");
+ pw.println(entry.second);
+ }
+ pw.decreaseIndent();
+ }
}
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 5c5f4ca..75d30a9 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -3228,6 +3228,12 @@
pw.increaseIndent();
mSkDestroyListener.dump(pw);
pw.decreaseIndent();
+
+ pw.println();
+ pw.println("NetworkStatsFactory logs:");
+ pw.increaseIndent();
+ mStatsFactory.dump(pw);
+ pw.decreaseIndent();
}
}
diff --git a/service/ServiceConnectivityResources/res/values/config_thread.xml b/service/ServiceConnectivityResources/res/values/config_thread.xml
index a458c7f..62b12fb 100644
--- a/service/ServiceConnectivityResources/res/values/config_thread.xml
+++ b/service/ServiceConnectivityResources/res/values/config_thread.xml
@@ -46,11 +46,12 @@
<bool name="config_thread_location_use_for_country_code_enabled">true</bool>
<!-- Specifies the UTF-8 vendor name of this device. If this value is not an empty string, it
- will be included in TXT value (key is 'vn') of the "_meshcop._udp" mDNS service which is
- published by the Thread service. A non-empty string value must not exceed length of 24 UTF-8
- bytes.
+ will be included in TXT value (key is 'vn') of the "_meshcop._udp" mDNS service as well as the
+ Vendor Name TLV for network diagnostic. A non-empty string value must not exceed length of 24
+ UTF-8 bytes. A special value "ro.product.manufacturer" indicates this value should be derived
+ from the `ro.product.manufacturer` system property.
-->
- <string translatable="false" name="config_thread_vendor_name">Android</string>
+ <string translatable="false" name="config_thread_vendor_name">ro.product.manufacturer</string>
<!-- Specifies the 24 bits vendor OUI of this device. If this value is not an empty string, it
will be included in TXT (key is 'vo') value of the "_meshcop._udp" mDNS service which is
@@ -61,11 +62,12 @@
<string translatable="false" name="config_thread_vendor_oui"></string>
<!-- Specifies the UTF-8 product model name of this device. If this value is not an empty
- string, it will be included in TXT (key is 'mn') value of the "_meshcop._udp" mDNS service
- which is published by the Thread service. A non-empty string value must not exceed length of 24
- UTF-8 bytes.
+ string, it will be included in TXT (key is 'mn') value of the "_meshcop._udp" mDNS service as
+ well as the Vendor Model TLV for network diagnostic. A non-empty string value must not exceed
+ length of 24 UTF-8 bytes. A special value "ro.product.model" indicates this value should be
+ derived from the `ro.product.model` system property.
-->
- <string translatable="false" name="config_thread_model_name">Thread Border Router</string>
+ <string translatable="false" name="config_thread_model_name">ro.product.model</string>
<!-- Specifies vendor-specific mDNS TXT entries which will be included in the "_meshcop._udp"
service. The TXT entries list MUST conform to the format requirement in RFC 6763 section 6. For
diff --git a/service/ServiceConnectivityResources/res/values/overlayable.xml b/service/ServiceConnectivityResources/res/values/overlayable.xml
index f6dbf6c..28b46c1 100644
--- a/service/ServiceConnectivityResources/res/values/overlayable.xml
+++ b/service/ServiceConnectivityResources/res/values/overlayable.xml
@@ -49,6 +49,7 @@
<!-- Configuration values for ThreadNetworkService -->
<item type="bool" name="config_thread_default_enabled" />
<item type="bool" name="config_thread_border_router_default_enabled" />
+ <item type="bool" name="config_thread_country_code_enabled" />
<item type="bool" name="config_thread_location_use_for_country_code_enabled" />
<item type="string" name="config_thread_vendor_name" />
<item type="string" name="config_thread_vendor_oui" />
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt b/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt
index c9d2527..076398e 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/CarrierConfigRule.kt
@@ -40,6 +40,7 @@
import com.android.testutils.runAsShell
import com.android.testutils.tryTest
import java.security.MessageDigest
+import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
import org.junit.rules.TestRule
@@ -204,6 +205,7 @@
}
return@tryTest
}
+ cv.close()
if (hold) {
addConfigOverrides(subId, PersistableBundle().also {
it.putStringArray(CarrierConfigManager.KEY_CARRIER_CERTIFICATE_STRING_ARRAY,
@@ -212,6 +214,9 @@
} else {
cleanUpNow()
}
+ assertTrue(cv.block(CARRIER_CONFIG_CHANGE_TIMEOUT_MS),
+ "Timed out waiting for CarrierPrivilegesCallback")
+ assertEquals(cpb.hasPrivilege, hold, "Couldn't set carrier privilege")
} cleanup @JvmSerializableLambda {
runAsShell(READ_PRIVILEGED_PHONE_STATE) @JvmSerializableLambda {
tm.unregisterCarrierPrivilegesCallback(cpb)
diff --git a/tests/common/java/android/net/NetworkCapabilitiesTest.java b/tests/common/java/android/net/NetworkCapabilitiesTest.java
index d694637..3fc2af0 100644
--- a/tests/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/common/java/android/net/NetworkCapabilitiesTest.java
@@ -56,6 +56,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
import static android.net.NetworkCapabilities.TRANSPORT_SATELLITE;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
+import static android.net.NetworkCapabilities.TRANSPORT_THREAD;
import static android.net.NetworkCapabilities.TRANSPORT_USB;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
@@ -1532,4 +1533,93 @@
nc.setReservationId(43);
assertNotEquals(nc, other);
}
+
+ @Test
+ public void testMatchNonThreadLocalNetworks_equals() {
+ final NetworkCapabilities nc = new NetworkCapabilities();
+ nc.setMatchNonThreadLocalNetworks(true);
+ final NetworkCapabilities other = new NetworkCapabilities(nc);
+ assertEquals(nc, other);
+
+ nc.setMatchNonThreadLocalNetworks(false);
+ assertNotEquals(nc, other);
+ }
+
+ @Test
+ public void testMatchNonThreadLocalNetworks_enabled() {
+ doTestMatchNonThreadLocalNetworks(true);
+ }
+
+ @Test
+ public void testMatchNonThreadLocalNetworks_disabled() {
+ doTestMatchNonThreadLocalNetworks(false);
+ }
+
+ private void doTestMatchNonThreadLocalNetworks(boolean enabled) {
+ // Setup request NCs.
+ final NetworkCapabilities noTransportRequestNc = new NetworkCapabilities();
+ final NetworkCapabilities threadRequestNc =
+ new NetworkCapabilities.Builder().addTransportType(TRANSPORT_THREAD).build();
+ final NetworkCapabilities wifiRequestNc =
+ new NetworkCapabilities.Builder().addTransportType(TRANSPORT_WIFI).build();
+ final NetworkCapabilities multiTransportRequestNc =
+ new NetworkCapabilities.Builder().addTransportType(
+ TRANSPORT_THREAD).addTransportType(TRANSPORT_WIFI).build();
+
+ // Setup network NCs.
+ final NetworkCapabilities localNoTransportNc = new NetworkCapabilities.Builder()
+ .addCapability(NET_CAPABILITY_LOCAL_NETWORK).build();
+ final NetworkCapabilities localThreadsNc = new NetworkCapabilities.Builder()
+ .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+ .addTransportType(TRANSPORT_THREAD).build();
+ final NetworkCapabilities localWifiNc = new NetworkCapabilities.Builder()
+ .addCapability(NET_CAPABILITY_LOCAL_NETWORK)
+ .addTransportType(TRANSPORT_WIFI).build();
+ final NetworkCapabilities wanWifiNc = new NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+
+ // Mark flags accordingly.
+ noTransportRequestNc.setMatchNonThreadLocalNetworks(enabled);
+ threadRequestNc.setMatchNonThreadLocalNetworks(enabled);
+ wifiRequestNc.setMatchNonThreadLocalNetworks(enabled);
+ multiTransportRequestNc.setMatchNonThreadLocalNetworks(enabled);
+
+ if (enabled) {
+ // A request with no specific transport matches all networks.
+ assertTrue(noTransportRequestNc.satisfiedByNetworkCapabilities(localNoTransportNc));
+ assertTrue(noTransportRequestNc.satisfiedByNetworkCapabilities(localWifiNc));
+ } else {
+ // A request with no specific transport only matches thread networks.
+ assertFalse(noTransportRequestNc.satisfiedByNetworkCapabilities(localNoTransportNc));
+ assertFalse(noTransportRequestNc.satisfiedByNetworkCapabilities(localWifiNc));
+ }
+ assertTrue(noTransportRequestNc.satisfiedByNetworkCapabilities(localThreadsNc));
+ assertTrue(noTransportRequestNc.satisfiedByNetworkCapabilities(wanWifiNc));
+
+ // A request with TRANSPORT_THREAD only matches thread networks.
+ assertFalse(threadRequestNc.satisfiedByNetworkCapabilities(localNoTransportNc));
+ assertTrue(threadRequestNc.satisfiedByNetworkCapabilities(localThreadsNc));
+ assertFalse(threadRequestNc.satisfiedByNetworkCapabilities(localWifiNc));
+ assertFalse(threadRequestNc.satisfiedByNetworkCapabilities(wanWifiNc));
+
+ assertFalse(multiTransportRequestNc.satisfiedByNetworkCapabilities(localNoTransportNc));
+ assertTrue(multiTransportRequestNc.satisfiedByNetworkCapabilities(localThreadsNc));
+ assertTrue(multiTransportRequestNc.satisfiedByNetworkCapabilities(wanWifiNc));
+ if (enabled) {
+ assertTrue(multiTransportRequestNc.satisfiedByNetworkCapabilities(localWifiNc));
+ } else {
+ // A request with multiple transports only matches thread networks.
+ assertFalse(multiTransportRequestNc.satisfiedByNetworkCapabilities(localWifiNc));
+ }
+
+ assertFalse(wifiRequestNc.satisfiedByNetworkCapabilities(localNoTransportNc));
+ assertFalse(wifiRequestNc.satisfiedByNetworkCapabilities(localThreadsNc));
+ assertTrue(wifiRequestNc.satisfiedByNetworkCapabilities(wanWifiNc));
+ if (enabled) {
+ assertTrue(wifiRequestNc.satisfiedByNetworkCapabilities(localWifiNc));
+ } else {
+ // A request without TRANSPORT_THREAD matches nothing.
+ assertFalse(wifiRequestNc.satisfiedByNetworkCapabilities(localWifiNc));
+ }
+ }
}
diff --git a/tests/cts/net/AndroidManifest.xml b/tests/cts/net/AndroidManifest.xml
index 098cc0a..acf89be 100644
--- a/tests/cts/net/AndroidManifest.xml
+++ b/tests/cts/net/AndroidManifest.xml
@@ -26,6 +26,7 @@
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+ <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />
<uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index 8f2ac7f..02a5d1f 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -15,6 +15,7 @@
*/
package android.net.cts
+import android.Manifest.permission.NEARBY_WIFI_DEVICES
import android.Manifest.permission.NETWORK_SETTINGS
import android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE
import android.app.Instrumentation
@@ -247,6 +248,12 @@
@Before
fun setUp() {
instrumentation.getUiAutomation().adoptShellPermissionIdentity()
+ if (SdkLevel.isAtLeastT()) {
+ instrumentation.getUiAutomation().grantRuntimePermission(
+ "android.net.cts",
+ NEARBY_WIFI_DEVICES
+ )
+ }
mHandlerThread.start()
}
@@ -751,12 +758,24 @@
tryTest {
// This process is not the carrier service UID, so allowedUids should be ignored in all
// the following cases.
- doTestAllowedUidsWithSubId(defaultSubId, TRANSPORT_CELLULAR, uid,
- expectUidsPresent = false)
- doTestAllowedUidsWithSubId(defaultSubId, TRANSPORT_WIFI, uid,
- expectUidsPresent = false)
- doTestAllowedUidsWithSubId(defaultSubId, TRANSPORT_BLUETOOTH, uid,
- expectUidsPresent = false)
+ doTestAllowedUidsWithSubId(
+ defaultSubId,
+ TRANSPORT_CELLULAR,
+ uid,
+ expectUidsPresent = false
+ )
+ doTestAllowedUidsWithSubId(
+ defaultSubId,
+ TRANSPORT_WIFI,
+ uid,
+ expectUidsPresent = false
+ )
+ doTestAllowedUidsWithSubId(
+ defaultSubId,
+ TRANSPORT_BLUETOOTH,
+ uid,
+ expectUidsPresent = false
+ )
// The tools to set the carrier service package override do not exist before U,
// so there is no way to test the rest of this test on < U.
@@ -774,9 +793,11 @@
val timeout = SystemClock.elapsedRealtime() + DEFAULT_TIMEOUT_MS
while (true) {
if (SystemClock.elapsedRealtime() > timeout) {
- fail("Couldn't make $servicePackage the service package for $defaultSubId: " +
+ fail(
+ "Couldn't make $servicePackage the service package for $defaultSubId: " +
"dumpsys connectivity".execute().split("\n")
- .filter { it.contains("Logical slot = $defaultSlotIndex.*") })
+ .filter { it.contains("Logical slot = $defaultSlotIndex.*") }
+ )
}
if ("dumpsys connectivity"
.execute()
@@ -799,10 +820,18 @@
// TODO(b/315136340): Allow ownerUid to see allowedUids and enable below test case
// doTestAllowedUids(defaultSubId, TRANSPORT_WIFI, uid, expectUidsPresent = true)
}
- doTestAllowedUidsWithSubId(defaultSubId, TRANSPORT_BLUETOOTH, uid,
- expectUidsPresent = false)
- doTestAllowedUidsWithSubId(defaultSubId, intArrayOf(TRANSPORT_CELLULAR, TRANSPORT_WIFI),
- uid, expectUidsPresent = false)
+ doTestAllowedUidsWithSubId(
+ defaultSubId,
+ TRANSPORT_BLUETOOTH,
+ uid,
+ expectUidsPresent = false
+ )
+ doTestAllowedUidsWithSubId(
+ defaultSubId,
+ intArrayOf(TRANSPORT_CELLULAR, TRANSPORT_WIFI),
+ uid,
+ expectUidsPresent = false
+ )
}
}
@@ -1884,8 +1913,10 @@
it.setTransportInfo(VpnTransportInfo(
VpnManager.TYPE_VPN_PLATFORM,
sessionId,
- /*bypassable=*/ false,
- /*longLivedTcpConnectionsExpensive=*/ false
+ /*bypassable=*/
+ false,
+ /*longLivedTcpConnectionsExpensive=*/
+ false
))
it.underlyingNetworks = listOf()
}
diff --git a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
index abe628b..92d58e6 100644
--- a/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
+++ b/tests/cts/tethering/src/android/tethering/cts/TetheringManagerTest.java
@@ -36,6 +36,7 @@
import static android.net.TetheringManager.TETHER_ERROR_ENTITLEMENT_UNKNOWN;
import static android.net.TetheringManager.TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION;
import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR;
+import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_REQUEST;
import static android.net.cts.util.CtsTetheringUtils.isAnyIfaceMatch;
import static android.os.Process.INVALID_UID;
@@ -714,4 +715,185 @@
assertThrows(UnsupportedOperationException.class, () -> mTM.tether("iface"));
assertThrows(UnsupportedOperationException.class, () -> mTM.untether("iface"));
}
+
+ @Test
+ public void testCarrierPrivilegedIsTetheringSupported() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastB());
+ assumeTrue(mPm.hasSystemFeature(FEATURE_TELEPHONY));
+ int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
+ mCarrierConfigRule.acquireCarrierPrivilege(defaultSubId);
+ final TestTetheringEventCallback tetherEventCallback =
+ mCtsTetheringUtils.registerTetheringEventCallback();
+ try {
+ tetherEventCallback.assumeWifiTetheringSupported(mContext);
+ tetherEventCallback.expectNoTetheringActive();
+
+ assertTrue(mTM.isTetheringSupported());
+ } finally {
+ mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
+ }
+ }
+
+ @Test
+ public void testCarrierPrivilegedStartTetheringNonWifiFails() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastB());
+ assumeTrue(mPm.hasSystemFeature(FEATURE_TELEPHONY));
+ int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
+ mCarrierConfigRule.acquireCarrierPrivilege(defaultSubId);
+ final TestTetheringEventCallback tetherEventCallback =
+ mCtsTetheringUtils.registerTetheringEventCallback();
+ try {
+ tetherEventCallback.assumeWifiTetheringSupported(mContext);
+ tetherEventCallback.expectNoTetheringActive();
+ StartTetheringCallback callback = new StartTetheringCallback();
+ TetheringRequest request = new TetheringRequest.Builder(TETHERING_USB).build();
+
+ mTM.startTethering(request, Runnable::run, callback);
+
+ callback.expectTetheringFailed(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ } finally {
+ mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
+ }
+ }
+
+ @Test
+ public void testCarrierPrivilegedStartTetheringWifiWithoutConfigFails() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastB());
+ assumeTrue(mPm.hasSystemFeature(FEATURE_TELEPHONY));
+ int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
+ mCarrierConfigRule.acquireCarrierPrivilege(defaultSubId);
+ final TestTetheringEventCallback tetherEventCallback =
+ mCtsTetheringUtils.registerTetheringEventCallback();
+ try {
+ tetherEventCallback.assumeWifiTetheringSupported(mContext);
+ tetherEventCallback.expectNoTetheringActive();
+ StartTetheringCallback callback = new StartTetheringCallback();
+ TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
+
+ mTM.startTethering(request, Runnable::run, callback);
+
+ callback.expectTetheringFailed(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ } finally {
+ mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
+ }
+ }
+
+ @Test
+ public void testCarrierPrivilegedStartTetheringWifiWithConfigSucceeds() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastB());
+ assumeTrue(mPm.hasSystemFeature(FEATURE_TELEPHONY));
+ int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
+ mCarrierConfigRule.acquireCarrierPrivilege(defaultSubId);
+ final TestTetheringEventCallback tetherEventCallback =
+ mCtsTetheringUtils.registerTetheringEventCallback();
+ try {
+ tetherEventCallback.assumeWifiTetheringSupported(mContext);
+ tetherEventCallback.expectNoTetheringActive();
+ SoftApConfiguration softApConfig = createSoftApConfiguration("Carrier-privileged");
+
+ mCtsTetheringUtils.startWifiTetheringNoPermissions(tetherEventCallback, softApConfig);
+ } finally {
+ mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
+ }
+ }
+
+ @Test
+ public void testCarrierPrivilegedStopTetheringNonWifiFails() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastB());
+ assumeTrue(mPm.hasSystemFeature(FEATURE_TELEPHONY));
+ int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
+ mCarrierConfigRule.acquireCarrierPrivilege(defaultSubId);
+ final TestTetheringEventCallback tetherEventCallback =
+ mCtsTetheringUtils.registerTetheringEventCallback();
+ try {
+ tetherEventCallback.assumeWifiTetheringSupported(mContext);
+ tetherEventCallback.expectNoTetheringActive();
+ TetheringRequest request = new TetheringRequest.Builder(TETHERING_USB).build();
+ CtsTetheringUtils.StopTetheringCallback
+ callback = new CtsTetheringUtils.StopTetheringCallback();
+
+ mTM.stopTethering(request, Runnable::run, callback);
+
+ callback.expectStopTetheringFailed(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ } finally {
+ mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
+ }
+ }
+
+ @Test
+ public void testCarrierPrivilegedStopTetheringWifiWithoutConfigFails() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastB());
+ assumeTrue(mPm.hasSystemFeature(FEATURE_TELEPHONY));
+ int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
+ mCarrierConfigRule.acquireCarrierPrivilege(defaultSubId);
+ final TestTetheringEventCallback tetherEventCallback =
+ mCtsTetheringUtils.registerTetheringEventCallback();
+ try {
+ tetherEventCallback.assumeWifiTetheringSupported(mContext);
+ tetherEventCallback.expectNoTetheringActive();
+ TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI).build();
+ CtsTetheringUtils.StopTetheringCallback
+ callback = new CtsTetheringUtils.StopTetheringCallback();
+
+ mTM.stopTethering(request, Runnable::run, callback);
+
+ callback.expectStopTetheringFailed(TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION);
+ } finally {
+ mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
+ }
+ }
+
+ @Test
+ public void testCarrierPrivilegedStopTetheringWifiWithConfigButNoActiveRequestFails()
+ throws Exception {
+ assumeTrue(SdkLevel.isAtLeastB());
+ assumeTrue(mPm.hasSystemFeature(FEATURE_TELEPHONY));
+ int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
+ mCarrierConfigRule.acquireCarrierPrivilege(defaultSubId);
+ final TestTetheringEventCallback tetherEventCallback =
+ mCtsTetheringUtils.registerTetheringEventCallback();
+ try {
+ tetherEventCallback.assumeWifiTetheringSupported(mContext);
+ tetherEventCallback.expectNoTetheringActive();
+ SoftApConfiguration softApConfig = createSoftApConfiguration("Carrier-privileged");
+ TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI)
+ .setSoftApConfiguration(softApConfig)
+ .build();
+ CtsTetheringUtils.StopTetheringCallback
+ callback = new CtsTetheringUtils.StopTetheringCallback();
+
+ mTM.stopTethering(request, Runnable::run, callback);
+
+ callback.expectStopTetheringFailed(TETHER_ERROR_UNKNOWN_REQUEST);
+ } finally {
+ mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
+ }
+ }
+
+ @Test
+ public void testCarrierPrivilegedStopTetheringWifiWithConfigSucceeds() throws Exception {
+ assumeTrue(SdkLevel.isAtLeastB());
+ assumeTrue(mPm.hasSystemFeature(FEATURE_TELEPHONY));
+ int defaultSubId = SubscriptionManager.getDefaultSubscriptionId();
+ mCarrierConfigRule.acquireCarrierPrivilege(defaultSubId);
+ final TestTetheringEventCallback tetherEventCallback =
+ mCtsTetheringUtils.registerTetheringEventCallback();
+ try {
+ tetherEventCallback.assumeWifiTetheringSupported(mContext);
+ tetherEventCallback.expectNoTetheringActive();
+ SoftApConfiguration softApConfig = createSoftApConfiguration("Carrier-privileged");
+ mCtsTetheringUtils.startWifiTetheringNoPermissions(tetherEventCallback, softApConfig);
+ TetheringRequest request = new TetheringRequest.Builder(TETHERING_WIFI)
+ .setSoftApConfiguration(softApConfig)
+ .build();
+ CtsTetheringUtils.StopTetheringCallback
+ callback = new CtsTetheringUtils.StopTetheringCallback();
+
+ mTM.stopTethering(request, Runnable::run, callback);
+
+ callback.verifyStopTetheringSucceeded();
+ } finally {
+ mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback);
+ }
+ }
}
diff --git a/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
index 63daebc..89acf69 100644
--- a/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -20,6 +20,7 @@
import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.METERED_ALL;
import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.METERED_YES;
import static android.net.NetworkStats.ROAMING_ALL;
import static android.net.NetworkStats.ROAMING_NO;
import static android.net.NetworkStats.SET_ALL;
@@ -29,6 +30,8 @@
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
+import static com.android.server.net.NetworkStatsFactory.CONFIG_PER_UID_TAG_THROTTLING;
+import static com.android.server.net.NetworkStatsFactory.CONFIG_PER_UID_TAG_THROTTLING_THRESHOLD;
import static com.android.server.net.NetworkStatsFactory.kernelToTag;
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
@@ -36,6 +39,9 @@
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import android.content.Context;
@@ -52,12 +58,15 @@
import com.android.server.BpfNetMaps;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
+import com.android.testutils.com.android.testutils.SetFeatureFlagsRule;
+import com.android.testutils.com.android.testutils.SetFeatureFlagsRule.FeatureFlag;
import libcore.io.IoUtils;
import libcore.testing.io.TestIoUtils;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -66,6 +75,7 @@
import java.io.File;
import java.io.IOException;
import java.net.ProtocolException;
+import java.util.HashMap;
/** Tests for {@link NetworkStatsFactory}. */
@RunWith(DevSdkIgnoreRunner.class)
@@ -73,6 +83,7 @@
@DevSdkIgnoreRule.IgnoreUpTo(SC_V2)
public class NetworkStatsFactoryTest extends NetworkStatsBaseTest {
private static final String CLAT_PREFIX = "v4-";
+ private static final int TEST_TAGS_PER_UID_THRESHOLD = 10;
private File mTestProc;
private NetworkStatsFactory mFactory;
@@ -80,6 +91,16 @@
@Mock private NetworkStatsFactory.Dependencies mDeps;
@Mock private BpfNetMaps mBpfNetMaps;
+ final HashMap<String, Boolean> mFeatureFlags = new HashMap<>();
+ // This will set feature flags from @FeatureFlag annotations
+ // into the map before setUp() runs.
+ @Rule
+ public final SetFeatureFlagsRule mSetFeatureFlagsRule =
+ new SetFeatureFlagsRule((name, enabled) -> {
+ mFeatureFlags.put(name, enabled);
+ return null;
+ }, (name) -> mFeatureFlags.getOrDefault(name, false));
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -90,6 +111,10 @@
// related to networkStatsFactory is compiled to a minimal native library and loaded here.
System.loadLibrary("networkstatsfactorytestjni");
doReturn(mBpfNetMaps).when(mDeps).createBpfNetMaps(any());
+ doAnswer(invocation -> mFeatureFlags.getOrDefault((String) invocation.getArgument(1), true))
+ .when(mDeps).isFeatureNotChickenedOut(any(), anyString());
+ doReturn(TEST_TAGS_PER_UID_THRESHOLD).when(mDeps)
+ .getDeviceConfigPropertyInt(eq(CONFIG_PER_UID_TAG_THROTTLING_THRESHOLD), anyInt());
mFactory = new NetworkStatsFactory(mContext, mDeps);
mFactory.updateUnderlyingNetworkInfos(new UnderlyingNetworkInfo[0]);
@@ -498,6 +523,71 @@
assertValues(removedUidsStats, TEST_IFACE, UID_GREEN, 64L, 3L, 1024L, 8L);
}
+ @FeatureFlag(name = CONFIG_PER_UID_TAG_THROTTLING)
+ @Test
+ public void testFilterTooManyTags_featureEnabled() throws Exception {
+ doTestFilterTooManyTags(true);
+ }
+
+ @FeatureFlag(name = CONFIG_PER_UID_TAG_THROTTLING, enabled = false)
+ @Test
+ public void testFilterTooManyTags_featureDisabled() throws Exception {
+ doTestFilterTooManyTags(false);
+ }
+
+ private void doTestFilterTooManyTags(boolean supportPerUidTagThrottling) throws Exception {
+ // Add entries for UID_RED which reaches the threshold.
+ final NetworkStats statsWithManyTags = new NetworkStats(0L, TEST_TAGS_PER_UID_THRESHOLD);
+ for (int tag = 1; tag <= TEST_TAGS_PER_UID_THRESHOLD; tag++) {
+ statsWithManyTags.combineValues(
+ new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, tag,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 12L, 18L, 14L, 1L, 0L));
+ }
+ doReturn(statsWithManyTags).when(mDeps).getNetworkStatsDetail();
+ final NetworkStats stats1 = mFactory.readNetworkStatsDetail();
+ assertEquals(stats1.size(), TEST_TAGS_PER_UID_THRESHOLD);
+
+ // Add 2 new entries with pre-existing tag, verify they can be added no matter what.
+ final NetworkStats newDiffWithExistingTag = new NetworkStats(0L, 2);
+ // This one should be added as a new entry, as the metered data doesn't exist yet.
+ newDiffWithExistingTag.combineValues(
+ new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
+ TEST_TAGS_PER_UID_THRESHOLD,
+ METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 3L, 5L, 8L, 1L, 1L));
+ // This one should be combined into existing entry.
+ newDiffWithExistingTag.combineValues(
+ new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
+ TEST_TAGS_PER_UID_THRESHOLD,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1L, 2L, 3L, 4L, 5L));
+
+ doReturn(newDiffWithExistingTag).when(mDeps).getNetworkStatsDetail();
+ final NetworkStats stats2 = mFactory.readNetworkStatsDetail();
+ assertEquals(stats2.size(), TEST_TAGS_PER_UID_THRESHOLD + 1);
+ assertValues(stats2, TEST_IFACE, UID_RED, SET_DEFAULT, TEST_TAGS_PER_UID_THRESHOLD,
+ METERED_YES, ROAMING_NO, DEFAULT_NETWORK_NO, 3L, 5L, 8L, 1L, 1L);
+ assertValues(stats2, TEST_IFACE, UID_RED, SET_DEFAULT, TEST_TAGS_PER_UID_THRESHOLD,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 13L, 20L, 17L, 5L, 5L);
+
+ // Add an entry which exceeds the threshold, verify the entry is filtered out.
+ final NetworkStats newDiffWithNonExistingTag = new NetworkStats(0L, 1);
+ newDiffWithNonExistingTag.combineValues(
+ new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT,
+ TEST_TAGS_PER_UID_THRESHOLD + 1,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 12L, 18L, 14L, 1L, 0L));
+ doReturn(newDiffWithNonExistingTag).when(mDeps).getNetworkStatsDetail();
+ final NetworkStats stats3 = mFactory.readNetworkStatsDetail();
+ if (supportPerUidTagThrottling) {
+ assertEquals(stats3.size(), TEST_TAGS_PER_UID_THRESHOLD + 1);
+ assertNoStatsEntry(stats3, TEST_IFACE, UID_RED, SET_DEFAULT,
+ TEST_TAGS_PER_UID_THRESHOLD + 1);
+ } else {
+ assertEquals(stats3.size(), TEST_TAGS_PER_UID_THRESHOLD + 2);
+ assertValues(stats3, TEST_IFACE, UID_RED, SET_DEFAULT,
+ TEST_TAGS_PER_UID_THRESHOLD + 1,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 12L, 18L, 14L, 1L, 0L);
+ }
+ }
+
private NetworkStats buildEmptyStats() {
return new NetworkStats(SystemClock.elapsedRealtime(), 0);
}
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
index d859fb2..e99c88e 100644
--- a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
@@ -128,6 +128,7 @@
import com.android.net.module.util.SharedLog;
import com.android.server.ServiceManagerWrapper;
import com.android.server.connectivity.ConnectivityResources;
+import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.thread.openthread.BackboneRouterState;
import com.android.server.thread.openthread.DnsTxtAttribute;
import com.android.server.thread.openthread.IChannelMasksReceiver;
@@ -191,6 +192,7 @@
private final Context mContext;
private final Handler mHandler;
+ private final MockableSystemProperties mSystemProperties;
// Below member fields can only be accessed from the handler thread (`mHandler`). In
// particular, the constructor does not run on the handler thread, so it must not touch any of
@@ -235,6 +237,7 @@
ThreadNetworkControllerService(
Context context,
Handler handler,
+ MockableSystemProperties systemProperties,
NetworkProvider networkProvider,
Supplier<IOtDaemon> otDaemonSupplier,
ConnectivityManager connectivityManager,
@@ -249,6 +252,7 @@
Map<Network, LinkProperties> networkToLinkProperties) {
mContext = context;
mHandler = handler;
+ mSystemProperties = systemProperties;
mNetworkProvider = networkProvider;
mOtDaemonSupplier = otDaemonSupplier;
mConnectivityManager = connectivityManager;
@@ -286,6 +290,7 @@
return new ThreadNetworkControllerService(
context,
handler,
+ new MockableSystemProperties(),
networkProvider,
() -> IOtDaemon.Stub.asInterface(ServiceManagerWrapper.waitForService("ot_daemon")),
connectivityManager,
@@ -355,7 +360,7 @@
newOtDaemonConfig(mPersistentSettings.getConfiguration()),
mTunIfController.getTunFd(),
mNsdPublisher,
- getMeshcopTxtAttributes(mResources.get()),
+ getMeshcopTxtAttributes(mResources.get(), mSystemProperties),
mCountryCodeSupplier.get(),
FeatureFlags.isTrelEnabled(),
mOtDaemonCallbackProxy);
@@ -365,10 +370,37 @@
return mOtDaemon;
}
+ static String getVendorName(Resources resources, MockableSystemProperties systemProperties) {
+ final String PROP_MANUFACTURER = "ro.product.manufacturer";
+ String vendorName = resources.getString(R.string.config_thread_vendor_name);
+ if (vendorName.equalsIgnoreCase(PROP_MANUFACTURER)) {
+ vendorName = systemProperties.get(PROP_MANUFACTURER);
+ // Assume it's always ASCII chars in ro.product.manufacturer
+ if (vendorName.length() > MAX_VENDOR_NAME_UTF8_BYTES) {
+ vendorName = vendorName.substring(0, MAX_VENDOR_NAME_UTF8_BYTES);
+ }
+ }
+ return vendorName;
+ }
+
+ static String getModelName(Resources resources, MockableSystemProperties systemProperties) {
+ final String PROP_MODEL = "ro.product.model";
+ String modelName = resources.getString(R.string.config_thread_model_name);
+ if (modelName.equalsIgnoreCase(PROP_MODEL)) {
+ modelName = systemProperties.get(PROP_MODEL);
+ // Assume it's always ASCII chars in ro.product.model
+ if (modelName.length() > MAX_MODEL_NAME_UTF8_BYTES) {
+ modelName = modelName.substring(0, MAX_MODEL_NAME_UTF8_BYTES);
+ }
+ }
+ return modelName;
+ }
+
@VisibleForTesting
- static MeshcopTxtAttributes getMeshcopTxtAttributes(Resources resources) {
- final String modelName = resources.getString(R.string.config_thread_model_name);
- final String vendorName = resources.getString(R.string.config_thread_vendor_name);
+ static MeshcopTxtAttributes getMeshcopTxtAttributes(
+ Resources resources, MockableSystemProperties systemProperties) {
+ final String vendorName = getVendorName(resources, systemProperties);
+ final String modelName = getModelName(resources, systemProperties);
final String vendorOui = resources.getString(R.string.config_thread_vendor_oui);
final String[] vendorSpecificTxts =
resources.getStringArray(R.array.config_thread_mdns_vendor_specific_txts);
@@ -637,6 +669,8 @@
.setSrpServerWaitForBorderRoutingEnabled(srpServerWaitEnabled)
.setBorderRouterAutoJoinEnabled(autoJoinEnabled)
.setCountryCodeEnabled(countryCodeEnabled)
+ .setVendorName(getVendorName(mResources.get(), mSystemProperties))
+ .setModelName(getModelName(mResources.get(), mSystemProperties))
.build();
}
diff --git a/thread/tests/integration/AndroidManifest.xml b/thread/tests/integration/AndroidManifest.xml
index a049184..8bee1e1 100644
--- a/thread/tests/integration/AndroidManifest.xml
+++ b/thread/tests/integration/AndroidManifest.xml
@@ -24,6 +24,7 @@
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.THREAD_NETWORK_PRIVILEGED"/>
<uses-permission android:name="android.permission.NETWORK_SETTINGS"/>
+ <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />
<uses-permission android:name="android.permission.INTERNET"/>
<application android:debuggable="true">
diff --git a/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java b/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
index c4e373a..a9c0da2 100644
--- a/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
+++ b/thread/tests/integration/src/android/net/thread/ServiceDiscoveryTest.java
@@ -48,6 +48,7 @@
import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
import android.net.thread.utils.ThreadNetworkControllerWrapper;
import android.os.HandlerThread;
+import android.os.SystemProperties;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.filters.LargeTest;
@@ -454,14 +455,22 @@
}
@Test
- public void meshcopOverlay_vendorAndModelNameAreSetToOverlayValue() throws Exception {
+ public void meshcopOverlay_vendorAndModelNameAreSetToSystemProperties() throws Exception {
NsdServiceInfo discoveredService = discoverService(mNsdManager, "_meshcop._udp");
assertThat(discoveredService).isNotNull();
NsdServiceInfo meshcopService = resolveService(mNsdManager, discoveredService);
+ String expectedVendorName = SystemProperties.get("ro.product.manufacturer");
+ if (expectedVendorName.length() > 24) {
+ expectedVendorName = expectedVendorName.substring(0, 24);
+ }
+ String expectedModelName = SystemProperties.get("ro.product.model");
+ if (expectedModelName.length() > 24) {
+ expectedModelName = expectedModelName.substring(0, 24);
+ }
Map<String, byte[]> txtMap = meshcopService.getAttributes();
- assertThat(txtMap.get("vn")).isEqualTo("Android".getBytes(UTF_8));
- assertThat(txtMap.get("mn")).isEqualTo("Thread Border Router".getBytes(UTF_8));
+ assertThat(txtMap.get("vn")).isEqualTo(expectedVendorName.getBytes(UTF_8));
+ assertThat(txtMap.get("mn")).isEqualTo(expectedModelName.getBytes(UTF_8));
}
@Test
diff --git a/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java b/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java
index b608c5d..f586f6e 100644
--- a/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java
+++ b/thread/tests/integration/src/android/net/thread/ThreadIntegrationTest.java
@@ -24,9 +24,13 @@
import static android.net.thread.utils.IntegrationTestUtils.getIpv6Addresses;
import static android.net.thread.utils.IntegrationTestUtils.getIpv6LinkAddresses;
import static android.net.thread.utils.IntegrationTestUtils.waitFor;
+import static android.net.thread.utils.OtDaemonController.DIAG_VENDOR_MODEL_TLV_TYPE;
+import static android.net.thread.utils.OtDaemonController.DIAG_VENDOR_NAME_TLV_TYPE;
import static android.net.thread.utils.ThreadNetworkControllerWrapper.JOIN_TIMEOUT;
import static android.os.SystemClock.elapsedRealtime;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
import static com.android.compatibility.common.util.SystemUtil.runShellCommandOrThrow;
import static com.android.testutils.TestPermissionUtil.runAsShell;
@@ -121,6 +125,12 @@
@Before
public void setUp() throws Exception {
+ getInstrumentation()
+ .getUiAutomation()
+ .grantRuntimePermission(
+ "com.android.thread.tests.integration",
+ "android.permission.NEARBY_WIFI_DEVICES");
+
mExecutor = Executors.newSingleThreadExecutor();
mFtd = new FullThreadDevice(10 /* nodeId */);
mOtCtl = new OtDaemonController();
@@ -290,6 +300,17 @@
// TODO: b/376217403 - enables / disables Border Agent at runtime
}
+ @Test
+ public void networkDiagnostic_vendorAndModelNameAreSet() throws Exception {
+ mController.joinAndWait(DEFAULT_DATASET);
+
+ var tlvTypes = List.of(DIAG_VENDOR_NAME_TLV_TYPE, DIAG_VENDOR_MODEL_TLV_TYPE);
+ var result = mOtCtl.netDiagGet(mOtCtl.getMlEid(), tlvTypes);
+
+ assertThat(result.get("Vendor Name")).isNotEmpty();
+ assertThat(result.get("Vendor Model")).isNotEmpty();
+ }
+
private NetworkCapabilities registerNetworkCallbackAndWait(NetworkRequest request)
throws Exception {
CompletableFuture<Network> networkFuture = new CompletableFuture<>();
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 63d6130..a73390c 100644
--- a/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
+++ b/thread/tests/unit/src/com/android/server/thread/ThreadNetworkControllerServiceTest.java
@@ -34,6 +34,7 @@
import static android.net.thread.ThreadNetworkManager.PERMISSION_THREAD_NETWORK_PRIVILEGED;
import static android.net.thread.ThreadNetworkManager.PERMISSION_THREAD_NETWORK_TESTING;
+import static com.android.server.thread.ThreadNetworkControllerService.getMeshcopTxtAttributes;
import static com.android.server.thread.ThreadNetworkCountryCode.DEFAULT_COUNTRY_CODE;
import static com.android.server.thread.ThreadPersistentSettings.KEY_THREAD_ENABLED;
import static com.android.server.thread.openthread.IOtDaemon.ErrorCode.OT_ERROR_INVALID_STATE;
@@ -49,8 +50,6 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.isNotNull;
-import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
@@ -101,10 +100,10 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.net.module.util.RoutingCoordinatorManager;
import com.android.server.connectivity.ConnectivityResources;
+import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.thread.openthread.DnsTxtAttribute;
import com.android.server.thread.openthread.IOtStatusReceiver;
import com.android.server.thread.openthread.MeshcopTxtAttributes;
-import com.android.server.thread.openthread.OtDaemonConfiguration;
import com.android.server.thread.openthread.testing.FakeOtDaemon;
import org.junit.After;
@@ -181,6 +180,7 @@
private static final String TEST_MODEL_NAME = "test model";
private static final LinkAddress TEST_NAT64_CIDR = new LinkAddress("192.168.255.0/24");
+ @Mock private MockableSystemProperties mMockSystemProperties;
@Mock private ConnectivityManager mMockConnectivityManager;
@Mock private RoutingCoordinatorManager mMockRoutingCoordinatorManager;
@Mock private NetworkAgent mMockNetworkAgent;
@@ -271,6 +271,7 @@
new ThreadNetworkControllerService(
mContext,
handler,
+ mMockSystemProperties,
networkProvider,
() -> mFakeOtDaemon,
mMockConnectivityManager,
@@ -336,6 +337,23 @@
}
@Test
+ public void initialize_vendorAndModelNameSetToProperty_propertiesAreSetToOtDaemon()
+ throws Exception {
+ when(mMockSystemProperties.get(eq("ro.product.manufacturer"))).thenReturn("Banana");
+ when(mResources.getString(eq(R.string.config_thread_vendor_name)))
+ .thenReturn("ro.product.manufacturer");
+ when(mMockSystemProperties.get(eq("ro.product.model"))).thenReturn("Orange");
+ when(mResources.getString(eq(R.string.config_thread_model_name)))
+ .thenReturn("ro.product.model");
+
+ mService.initialize();
+ mTestLooper.dispatchAll();
+
+ assertThat(mFakeOtDaemon.getConfiguration().vendorName).isEqualTo("Banana");
+ assertThat(mFakeOtDaemon.getConfiguration().modelName).isEqualTo("Orange");
+ }
+
+ @Test
public void initialize_nat64Disabled_doesNotRequestNat64CidrAndConfiguresOtDaemon()
throws Exception {
ThreadConfiguration config =
@@ -345,8 +363,7 @@
mTestLooper.dispatchAll();
verify(mMockRoutingCoordinatorManager, never()).requestDownstreamAddress(any());
- verify(mFakeOtDaemon, times(1)).setNat64Cidr(isNull(), any());
- verify(mFakeOtDaemon, never()).setNat64Cidr(isNotNull(), any());
+ assertThat(mFakeOtDaemon.getNat64Cidr()).isNull();
}
@Test
@@ -359,11 +376,8 @@
mTestLooper.dispatchAll();
verify(mMockRoutingCoordinatorManager, times(1)).requestDownstreamAddress(any());
- verify(mFakeOtDaemon, times(1))
- .setConfiguration(
- new OtDaemonConfiguration.Builder().setNat64Enabled(true).build(),
- null /* receiver */);
- verify(mFakeOtDaemon, times(1)).setNat64Cidr(eq(TEST_NAT64_CIDR.toString()), any());
+ assertThat(mFakeOtDaemon.getConfiguration().nat64Enabled).isTrue();
+ assertThat(mFakeOtDaemon.getNat64Cidr()).isEqualTo(TEST_NAT64_CIDR.toString());
}
@Test
@@ -400,7 +414,7 @@
when(mResources.getString(eq(R.string.config_thread_vendor_name))).thenReturn("");
MeshcopTxtAttributes meshcopTxts =
- ThreadNetworkControllerService.getMeshcopTxtAttributes(mResources);
+ getMeshcopTxtAttributes(mResources, mMockSystemProperties);
assertThat(meshcopTxts.vendorName).isEqualTo("");
}
@@ -412,7 +426,31 @@
assertThrows(
IllegalStateException.class,
- () -> ThreadNetworkControllerService.getMeshcopTxtAttributes(mResources));
+ () -> getMeshcopTxtAttributes(mResources, mMockSystemProperties));
+ }
+
+ @Test
+ public void getMeshcopTxtAttributes_VendorNameSetToManufacturer_manufacturerPropertyIsUsed() {
+ when(mMockSystemProperties.get(eq("ro.product.manufacturer"))).thenReturn("Banana");
+ when(mResources.getString(eq(R.string.config_thread_vendor_name)))
+ .thenReturn("ro.product.manufacturer");
+
+ MeshcopTxtAttributes meshcopTxts =
+ getMeshcopTxtAttributes(mResources, mMockSystemProperties);
+
+ assertThat(meshcopTxts.vendorName).isEqualTo("Banana");
+ }
+
+ @Test
+ public void getMeshcopTxtAttributes_ModelNameSetToModelProperty_modelPropertyIsUsed() {
+ when(mMockSystemProperties.get(eq("ro.product.model"))).thenReturn("Orange");
+ when(mResources.getString(eq(R.string.config_thread_model_name)))
+ .thenReturn("ro.product.model");
+
+ MeshcopTxtAttributes meshcopTxts =
+ getMeshcopTxtAttributes(mResources, mMockSystemProperties);
+
+ assertThat(meshcopTxts.modelName).isEqualTo("Orange");
}
@Test
@@ -422,14 +460,14 @@
assertThrows(
IllegalStateException.class,
- () -> ThreadNetworkControllerService.getMeshcopTxtAttributes(mResources));
+ () -> getMeshcopTxtAttributes(mResources, mMockSystemProperties));
}
@Test
public void getMeshcopTxtAttributes_emptyModelName_accepted() {
when(mResources.getString(eq(R.string.config_thread_model_name))).thenReturn("");
- var meshcopTxts = ThreadNetworkControllerService.getMeshcopTxtAttributes(mResources);
+ var meshcopTxts = getMeshcopTxtAttributes(mResources, mMockSystemProperties);
assertThat(meshcopTxts.modelName).isEqualTo("");
}
@@ -461,7 +499,7 @@
private byte[] getMeshcopTxtAttributesWithVendorOui(String vendorOui) {
when(mResources.getString(eq(R.string.config_thread_vendor_oui))).thenReturn(vendorOui);
- return ThreadNetworkControllerService.getMeshcopTxtAttributes(mResources).vendorOui;
+ return getMeshcopTxtAttributes(mResources, mMockSystemProperties).vendorOui;
}
@Test
@@ -886,16 +924,12 @@
verify(mockReceiver, times(1)).onSuccess();
verify(mMockRoutingCoordinatorManager, times(1)).requestDownstreamAddress(any());
- verify(mFakeOtDaemon, times(1))
- .setConfiguration(
- eq(new OtDaemonConfiguration.Builder().setNat64Enabled(true).build()),
- any(IOtStatusReceiver.class));
- verify(mFakeOtDaemon, times(1))
- .setNat64Cidr(eq(TEST_NAT64_CIDR.toString()), any(IOtStatusReceiver.class));
+ assertThat(mFakeOtDaemon.getConfiguration().nat64Enabled).isTrue();
+ assertThat(mFakeOtDaemon.getNat64Cidr()).isEqualTo(TEST_NAT64_CIDR.toString());
}
@Test
- public void setConfiguration_enablesNat64_otDaemonRemoteFailure_serviceDoesNotCrash()
+ public void setConfiguration_enablesNat64AndOtDaemonRemoteFailure_serviceDoesNotCrash()
throws Exception {
mService.initialize();
mTestLooper.dispatchAll();
@@ -929,12 +963,8 @@
verify(mockReceiver, times(1)).onSuccess();
verify(mMockRoutingCoordinatorManager, times(1)).releaseDownstream(any());
verify(mMockRoutingCoordinatorManager, never()).requestDownstreamAddress(any());
- verify(mFakeOtDaemon, times(1))
- .setConfiguration(
- eq(new OtDaemonConfiguration.Builder().setNat64Enabled(false).build()),
- any(IOtStatusReceiver.class));
- verify(mFakeOtDaemon, times(1)).setNat64Cidr(isNull(), any(IOtStatusReceiver.class));
- verify(mFakeOtDaemon, never()).setNat64Cidr(isNotNull(), any(IOtStatusReceiver.class));
+ assertThat(mFakeOtDaemon.getConfiguration().nat64Enabled).isFalse();
+ assertThat(mFakeOtDaemon.getNat64Cidr()).isNull();
}
@Test
diff --git a/thread/tests/utils/src/android/net/thread/utils/OtDaemonController.java b/thread/tests/utils/src/android/net/thread/utils/OtDaemonController.java
index d35b94e..41d9eaf 100644
--- a/thread/tests/utils/src/android/net/thread/utils/OtDaemonController.java
+++ b/thread/tests/utils/src/android/net/thread/utils/OtDaemonController.java
@@ -24,10 +24,13 @@
import com.android.compatibility.common.util.SystemUtil;
import java.net.Inet6Address;
+import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.stream.Collectors;
/**
@@ -37,6 +40,9 @@
* <p>Note that this class takes root privileged to run.
*/
public final class OtDaemonController {
+ public static final int DIAG_VENDOR_NAME_TLV_TYPE = 25;
+ public static final int DIAG_VENDOR_MODEL_TLV_TYPE = 26;
+
private static final String OT_CTL = "/system/bin/ot-ctl";
/**
@@ -188,6 +194,35 @@
}
/**
+ * Sends DIAG_GET request to the given peer device and returns the parsed result as a dict of
+ * the requested TLV values.
+ *
+ * <p>For example, a request {@code netDiagGet("fdad:3d13:7b11:4049:ed1a:7e87:4770:a345",
+ * [DIAG_VENDOR_NAME_TLV_TYPE, DIAG_VENDOR_MODEL_TLV_TYPE])} can return a dict of {@code
+ * {"Vendor Name" : "ABC", "Vendor Model" : "Cuttlefish"}}
+ */
+ public Map<String, String> netDiagGet(InetAddress peerAddr, List<Integer> tlvTypes) {
+ String tlvTypeList =
+ tlvTypes.stream().map(Object::toString).collect(Collectors.joining(" "));
+
+ List<String> outputs =
+ executeCommandAndParse(
+ "networkdiagnostic get " + peerAddr.getHostAddress() + " " + tlvTypeList);
+ Map<String, String> result = new HashMap<>();
+ for (String line : outputs) {
+ if (line.startsWith("DIAG_GET")) {
+ continue;
+ }
+ String[] keyValue = line.split(":");
+ if (keyValue.length != 2) {
+ throw new IllegalStateException("Unexpected OT output: " + line);
+ }
+ result.put(keyValue[0].strip(), keyValue[1].strip());
+ }
+ return result;
+ }
+
+ /**
* Executes a ot-ctl command and parse the output to a list of strings.
*
* <p>The trailing "Done" in the command output will be dropped.
diff --git a/thread/tests/utils/src/android/net/thread/utils/TapTestNetworkTracker.java b/thread/tests/utils/src/android/net/thread/utils/TapTestNetworkTracker.java
index 9a1a05b..62c2785 100644
--- a/thread/tests/utils/src/android/net/thread/utils/TapTestNetworkTracker.java
+++ b/thread/tests/utils/src/android/net/thread/utils/TapTestNetworkTracker.java
@@ -16,44 +16,23 @@
package android.net.thread.utils;
import static android.Manifest.permission.MANAGE_TEST_NETWORKS;
-import static android.net.InetAddresses.parseNumericAddress;
-import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
-import static android.net.NetworkCapabilities.TRANSPORT_TEST;
-import static android.system.OsConstants.AF_INET6;
-import static android.system.OsConstants.IPPROTO_UDP;
-import static android.system.OsConstants.SOCK_DGRAM;
import static com.android.testutils.RecorderCallback.CallbackEntry.LINK_PROPERTIES_CHANGED;
import static com.android.testutils.TestPermissionUtil.runAsShell;
import android.content.Context;
import android.net.ConnectivityManager;
-import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
-import android.net.NetworkAgentConfig;
-import android.net.NetworkCapabilities;
-import android.net.NetworkRequest;
import android.net.TestNetworkInterface;
import android.net.TestNetworkManager;
-import android.net.TestNetworkSpecifier;
import android.os.Looper;
-import android.system.ErrnoException;
-import android.system.Os;
-import com.android.compatibility.common.util.PollingCheck;
import com.android.testutils.TestableNetworkAgent;
import com.android.testutils.TestableNetworkCallback;
-import java.io.FileDescriptor;
import java.io.IOException;
-import java.net.InterfaceAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
/** A class that can create/destroy a test network based on TAP interface. */
public final class TapTestNetworkTracker {