summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/vcn/Vcn.java152
-rw-r--r--services/core/java/com/android/server/vcn/VcnGatewayConnection.java22
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java1
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java28
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java1
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnTest.java142
6 files changed, 326 insertions, 20 deletions
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 7bc6056f91f3..70a8a7ff7bb3 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -16,6 +16,8 @@
package com.android.server.vcn;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
@@ -26,14 +28,20 @@ import static com.android.server.VcnManagementService.VDBG;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.net.Uri;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnManager.VcnErrorCode;
import android.os.Handler;
import android.os.Message;
import android.os.ParcelUuid;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -42,9 +50,11 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
+import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
@@ -61,6 +71,9 @@ import java.util.Set;
public class Vcn extends Handler {
private static final String TAG = Vcn.class.getSimpleName();
+ private static final List<Integer> CAPS_REQUIRING_MOBILE_DATA =
+ Arrays.asList(NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN);
+
private static final int MSG_EVENT_BASE = 0;
private static final int MSG_CMD_BASE = 100;
@@ -110,6 +123,15 @@ public class Vcn extends Handler {
*/
private static final int MSG_EVENT_SAFE_MODE_STATE_CHANGED = MSG_EVENT_BASE + 4;
+ /**
+ * Triggers reevaluation of mobile data enabled conditions.
+ *
+ * <p>Upon this notification, the VCN will check if any of the underlying subIds have mobile
+ * data enabled. If not, the VCN will restart any GatewayConnections providing INTERNET or DUN
+ * with the current mobile data toggle status.
+ */
+ private static final int MSG_EVENT_MOBILE_DATA_TOGGLED = MSG_EVENT_BASE + 5;
+
/** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
@@ -118,6 +140,8 @@ public class Vcn extends Handler {
@NonNull private final Dependencies mDeps;
@NonNull private final VcnNetworkRequestListener mRequestListener;
@NonNull private final VcnCallback mVcnCallback;
+ @NonNull private final VcnContentResolver mContentResolver;
+ @NonNull private final ContentObserver mMobileDataSettingsObserver;
/**
* Map containing all VcnGatewayConnections and their VcnGatewayConnectionConfigs.
@@ -154,6 +178,8 @@ public class Vcn extends Handler {
// Accessed from different threads, but always under lock in VcnManagementService
private volatile int mCurrentStatus = VCN_STATUS_CODE_ACTIVE;
+ private boolean mIsMobileDataEnabled = false;
+
public Vcn(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@@ -177,10 +203,19 @@ public class Vcn extends Handler {
mVcnCallback = Objects.requireNonNull(vcnCallback, "Missing vcnCallback");
mDeps = Objects.requireNonNull(deps, "Missing deps");
mRequestListener = new VcnNetworkRequestListener();
+ mContentResolver = mDeps.newVcnContentResolver(mVcnContext);
+ mMobileDataSettingsObserver = new VcnMobileDataContentObserver(this /* handler */);
+
+ final Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA);
+ mContentResolver.registerContentObserver(
+ uri, true /* notifyForDescendants */, mMobileDataSettingsObserver);
mConfig = Objects.requireNonNull(config, "Missing config");
mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
+ // Update mIsMobileDataEnabled before starting handling of NetworkRequests.
+ mIsMobileDataEnabled = getMobileDataStatus();
+
// Register to receive cached and future NetworkRequests
mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener);
}
@@ -260,6 +295,9 @@ public class Vcn extends Handler {
case MSG_EVENT_SAFE_MODE_STATE_CHANGED:
handleSafeModeStatusChanged();
break;
+ case MSG_EVENT_MOBILE_DATA_TOGGLED:
+ handleMobileDataToggled();
+ break;
case MSG_CMD_TEARDOWN:
handleTeardown();
break;
@@ -366,18 +404,37 @@ public class Vcn extends Handler {
if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) {
Slog.v(getLogTag(), "Bringing up new VcnGatewayConnection for request " + request);
+ if (getExposedCapabilitiesForMobileDataState(gatewayConnectionConfig).isEmpty()) {
+ // Skip; this network does not provide any services if mobile data is disabled.
+ continue;
+ }
+
final VcnGatewayConnection vcnGatewayConnection =
mDeps.newVcnGatewayConnection(
mVcnContext,
mSubscriptionGroup,
mLastSnapshot,
gatewayConnectionConfig,
- new VcnGatewayStatusCallbackImpl(gatewayConnectionConfig));
+ new VcnGatewayStatusCallbackImpl(gatewayConnectionConfig),
+ mIsMobileDataEnabled);
mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection);
}
}
}
+ private Set<Integer> getExposedCapabilitiesForMobileDataState(
+ VcnGatewayConnectionConfig gatewayConnectionConfig) {
+ if (mIsMobileDataEnabled) {
+ return gatewayConnectionConfig.getAllExposedCapabilities();
+ }
+
+ final Set<Integer> exposedCapsWithoutMobileData =
+ new ArraySet<>(gatewayConnectionConfig.getAllExposedCapabilities());
+ exposedCapsWithoutMobileData.removeAll(CAPS_REQUIRING_MOBILE_DATA);
+
+ return exposedCapsWithoutMobileData;
+ }
+
private void handleGatewayConnectionQuit(VcnGatewayConnectionConfig config) {
Slog.v(getLogTag(), "VcnGatewayConnection quit: " + config);
mVcnGatewayConnections.remove(config);
@@ -396,12 +453,55 @@ public class Vcn extends Handler {
}
}
+ private void handleMobileDataToggled() {
+ final boolean oldMobileDataEnabledStatus = mIsMobileDataEnabled;
+ mIsMobileDataEnabled = getMobileDataStatus();
+
+ if (oldMobileDataEnabledStatus != mIsMobileDataEnabled) {
+ // Teardown any GatewayConnections that advertise INTERNET or DUN. If they provide other
+ // services, the VcnGatewayConnections will be restarted without advertising INTERNET or
+ // DUN.
+ for (Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry :
+ mVcnGatewayConnections.entrySet()) {
+ final VcnGatewayConnectionConfig gatewayConnectionConfig = entry.getKey();
+ final VcnGatewayConnection gatewayConnection = entry.getValue();
+
+ final Set<Integer> exposedCaps =
+ gatewayConnectionConfig.getAllExposedCapabilities();
+ if (exposedCaps.contains(NET_CAPABILITY_INTERNET)
+ || exposedCaps.contains(NET_CAPABILITY_DUN)) {
+ if (gatewayConnection == null) {
+ Slog.wtf(
+ getLogTag(),
+ "Found gatewayConnectionConfig without GatewayConnection");
+ } else {
+ // TODO(b/184868850): Optimize by restarting NetworkAgents without teardown.
+ gatewayConnection.teardownAsynchronously();
+ }
+ }
+ }
+ }
+ }
+
+ private boolean getMobileDataStatus() {
+ final TelephonyManager genericTelMan =
+ mVcnContext.getContext().getSystemService(TelephonyManager.class);
+
+ for (int subId : mLastSnapshot.getAllSubIdsInGroup(mSubscriptionGroup)) {
+ if (genericTelMan.createForSubscriptionId(subId).isDataEnabled()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
private boolean isRequestSatisfiedByGatewayConnectionConfig(
@NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) {
final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
builder.addTransportType(TRANSPORT_CELLULAR);
builder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
- for (int cap : config.getAllExposedCapabilities()) {
+ for (int cap : getExposedCapabilitiesForMobileDataState(config)) {
builder.addCapability(cap);
}
@@ -432,6 +532,16 @@ public class Vcn extends Handler {
pw.decreaseIndent();
}
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public boolean isMobileDataEnabled() {
+ return mIsMobileDataEnabled;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public void setMobileDataEnabled(boolean isMobileDataEnabled) {
+ mIsMobileDataEnabled = isMobileDataEnabled;
+ }
+
/** Retrieves the network score for a VCN Network */
// Package visibility for use in VcnGatewayConnection
static int getNetworkScore() {
@@ -485,6 +595,17 @@ public class Vcn extends Handler {
}
}
+ private class VcnMobileDataContentObserver extends ContentObserver {
+ private VcnMobileDataContentObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ sendMessage(obtainMessage(MSG_EVENT_MOBILE_DATA_TOGGLED));
+ }
+ }
+
/** External dependencies used by Vcn, for injection in tests */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class Dependencies {
@@ -494,13 +615,36 @@ public class Vcn extends Handler {
ParcelUuid subscriptionGroup,
TelephonySubscriptionSnapshot snapshot,
VcnGatewayConnectionConfig connectionConfig,
- VcnGatewayStatusCallback gatewayStatusCallback) {
+ VcnGatewayStatusCallback gatewayStatusCallback,
+ boolean isMobileDataEnabled) {
return new VcnGatewayConnection(
vcnContext,
subscriptionGroup,
snapshot,
connectionConfig,
- gatewayStatusCallback);
+ gatewayStatusCallback,
+ isMobileDataEnabled);
+ }
+
+ /** Builds a new VcnContentResolver instance */
+ public VcnContentResolver newVcnContentResolver(VcnContext vcnContext) {
+ return new VcnContentResolver(vcnContext);
+ }
+ }
+
+ /** Proxy Implementation of NetworkAgent, used for testing. */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class VcnContentResolver {
+ private final ContentResolver mImpl;
+
+ public VcnContentResolver(VcnContext vcnContext) {
+ mImpl = vcnContext.getContext().getContentResolver();
+ }
+
+ /** Registers the content observer */
+ public void registerContentObserver(
+ @NonNull Uri uri, boolean notifyForDescendants, @NonNull ContentObserver observer) {
+ mImpl.registerContentObserver(uri, notifyForDescendants, observer);
}
}
}
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index df31221b5d60..8847dda71c49 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -16,6 +16,8 @@
package com.android.server.vcn;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
@@ -517,6 +519,7 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull private final VcnGatewayStatusCallback mGatewayStatusCallback;
@NonNull private final Dependencies mDeps;
@NonNull private final VcnUnderlyingNetworkTrackerCallback mUnderlyingNetworkTrackerCallback;
+ private final boolean mIsMobileDataEnabled;
@NonNull private final IpSecManager mIpSecManager;
@@ -626,13 +629,15 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull ParcelUuid subscriptionGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull VcnGatewayConnectionConfig connectionConfig,
- @NonNull VcnGatewayStatusCallback gatewayStatusCallback) {
+ @NonNull VcnGatewayStatusCallback gatewayStatusCallback,
+ boolean isMobileDataEnabled) {
this(
vcnContext,
subscriptionGroup,
snapshot,
connectionConfig,
gatewayStatusCallback,
+ isMobileDataEnabled,
new Dependencies());
}
@@ -643,6 +648,7 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull TelephonySubscriptionSnapshot snapshot,
@NonNull VcnGatewayConnectionConfig connectionConfig,
@NonNull VcnGatewayStatusCallback gatewayStatusCallback,
+ boolean isMobileDataEnabled,
@NonNull Dependencies deps) {
super(TAG, Objects.requireNonNull(vcnContext, "Missing vcnContext").getLooper());
mVcnContext = vcnContext;
@@ -650,6 +656,7 @@ public class VcnGatewayConnection extends StateMachine {
mConnectionConfig = Objects.requireNonNull(connectionConfig, "Missing connectionConfig");
mGatewayStatusCallback =
Objects.requireNonNull(gatewayStatusCallback, "Missing gatewayStatusCallback");
+ mIsMobileDataEnabled = isMobileDataEnabled;
mDeps = Objects.requireNonNull(deps, "Missing deps");
mLastSnapshot = Objects.requireNonNull(snapshot, "Missing snapshot");
@@ -1502,7 +1509,7 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull VcnNetworkAgent agent,
@NonNull VcnChildSessionConfiguration childConfig) {
final NetworkCapabilities caps =
- buildNetworkCapabilities(mConnectionConfig, mUnderlying);
+ buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled);
final LinkProperties lp =
buildConnectedLinkProperties(
mConnectionConfig, tunnelIface, childConfig, mUnderlying);
@@ -1515,7 +1522,7 @@ public class VcnGatewayConnection extends StateMachine {
@NonNull IpSecTunnelInterface tunnelIface,
@NonNull VcnChildSessionConfiguration childConfig) {
final NetworkCapabilities caps =
- buildNetworkCapabilities(mConnectionConfig, mUnderlying);
+ buildNetworkCapabilities(mConnectionConfig, mUnderlying, mIsMobileDataEnabled);
final LinkProperties lp =
buildConnectedLinkProperties(
mConnectionConfig, tunnelIface, childConfig, mUnderlying);
@@ -1843,7 +1850,8 @@ public class VcnGatewayConnection extends StateMachine {
@VisibleForTesting(visibility = Visibility.PRIVATE)
static NetworkCapabilities buildNetworkCapabilities(
@NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
- @Nullable UnderlyingNetworkRecord underlying) {
+ @Nullable UnderlyingNetworkRecord underlying,
+ boolean isMobileDataEnabled) {
final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
builder.addTransportType(TRANSPORT_CELLULAR);
@@ -1853,6 +1861,12 @@ public class VcnGatewayConnection extends StateMachine {
// Add exposed capabilities
for (int cap : gatewayConnectionConfig.getAllExposedCapabilities()) {
+ // Skip adding INTERNET or DUN if mobile data is disabled.
+ if (!isMobileDataEnabled
+ && (cap == NET_CAPABILITY_INTERNET || cap == NET_CAPABILITY_DUN)) {
+ continue;
+ }
+
builder.addCapability(cap);
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
index 5f27fabb62b0..ac0edaa3b579 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
@@ -65,6 +65,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect
TEST_SUBSCRIPTION_SNAPSHOT,
mConfig,
mGatewayStatusCallback,
+ true /* isMobileDataEnabled */,
mDeps);
vgc.setIsQuitting(true);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index ced8745e89c9..9705f0fc6bbc 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -16,6 +16,8 @@
package com.android.server.vcn;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
@@ -89,7 +91,8 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
doReturn(mWifiInfo).when(mWifiInfo).makeCopy(anyLong());
}
- private void verifyBuildNetworkCapabilitiesCommon(int transportType) {
+ private void verifyBuildNetworkCapabilitiesCommon(
+ int transportType, boolean isMobileDataEnabled) {
final NetworkCapabilities.Builder capBuilder = new NetworkCapabilities.Builder();
capBuilder.addTransportType(transportType);
capBuilder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
@@ -109,10 +112,22 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
capBuilder.build(), new LinkProperties(), false);
final NetworkCapabilities vcnCaps =
VcnGatewayConnection.buildNetworkCapabilities(
- VcnGatewayConnectionConfigTest.buildTestConfig(), record);
+ VcnGatewayConnectionConfigTest.buildTestConfig(),
+ record,
+ isMobileDataEnabled);
+
assertTrue(vcnCaps.hasTransport(TRANSPORT_CELLULAR));
assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_METERED));
assertTrue(vcnCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+
+ for (int cap : VcnGatewayConnectionConfigTest.EXPOSED_CAPS) {
+ if (cap == NET_CAPABILITY_INTERNET || cap == NET_CAPABILITY_DUN) {
+ assertEquals(isMobileDataEnabled, vcnCaps.hasCapability(cap));
+ } else {
+ assertTrue(vcnCaps.hasCapability(cap));
+ }
+ }
+
assertArrayEquals(new int[] {TEST_UID}, vcnCaps.getAdministratorUids());
assertTrue(vcnCaps.getTransportInfo() instanceof VcnTransportInfo);
@@ -126,12 +141,17 @@ public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
@Test
public void testBuildNetworkCapabilitiesUnderlyingWifi() throws Exception {
- verifyBuildNetworkCapabilitiesCommon(TRANSPORT_WIFI);
+ verifyBuildNetworkCapabilitiesCommon(TRANSPORT_WIFI, true /* isMobileDataEnabled */);
}
@Test
public void testBuildNetworkCapabilitiesUnderlyingCell() throws Exception {
- verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR);
+ verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR, true /* isMobileDataEnabled */);
+ }
+
+ @Test
+ public void testBuildNetworkCapabilitiesMobileDataDisabled() throws Exception {
+ verifyBuildNetworkCapabilitiesCommon(TRANSPORT_CELLULAR, false /* isMobileDataEnabled */);
}
@Test
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 98d553dab01a..284f1f88658e 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -202,6 +202,7 @@ public class VcnGatewayConnectionTestBase {
TEST_SUBSCRIPTION_SNAPSHOT,
mConfig,
mGatewayStatusCallback,
+ true /* isMobileDataEnabled */,
mDeps);
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index 90eb75e865e7..7c2a5a1cfc5d 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -16,16 +16,24 @@
package com.android.server.vcn;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
+import static com.android.server.vcn.Vcn.VcnContentResolver;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyBoolean;
+import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
@@ -35,12 +43,16 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
+import android.database.ContentObserver;
import android.net.NetworkRequest;
+import android.net.Uri;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
import android.util.ArraySet;
import com.android.server.VcnManagementService.VcnCallback;
@@ -53,7 +65,10 @@ import org.junit.Test;
import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
@@ -62,14 +77,21 @@ public class VcnTest {
private static final ParcelUuid TEST_SUB_GROUP = new ParcelUuid(new UUID(0, 0));
private static final int NETWORK_SCORE = 0;
private static final int PROVIDER_ID = 5;
+ private static final boolean MOBILE_DATA_ENABLED = true;
+ private static final Set<Integer> TEST_SUB_IDS_IN_GROUP =
+ new ArraySet<>(Arrays.asList(1, 2, 3));
private static final int[][] TEST_CAPS =
new int[][] {
- new int[] {NET_CAPABILITY_MMS, NET_CAPABILITY_INTERNET},
- new int[] {NET_CAPABILITY_DUN}
+ new int[] {NET_CAPABILITY_IMS, NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN},
+ new int[] {NET_CAPABILITY_CBS, NET_CAPABILITY_INTERNET},
+ new int[] {NET_CAPABILITY_FOTA, NET_CAPABILITY_DUN},
+ new int[] {NET_CAPABILITY_MMS}
};
private Context mContext;
private VcnContext mVcnContext;
+ private TelephonyManager mTelephonyManager;
+ private VcnContentResolver mContentResolver;
private TelephonySubscriptionSnapshot mSubscriptionSnapshot;
private VcnNetworkProvider mVcnNetworkProvider;
private VcnCallback mVcnCallback;
@@ -86,6 +108,9 @@ public class VcnTest {
public void setUp() {
mContext = mock(Context.class);
mVcnContext = mock(VcnContext.class);
+ mTelephonyManager =
+ setupAndGetTelephonyManager(MOBILE_DATA_ENABLED /* isMobileDataEnabled */);
+ mContentResolver = mock(VcnContentResolver.class);
mSubscriptionSnapshot = mock(TelephonySubscriptionSnapshot.class);
mVcnNetworkProvider = mock(VcnNetworkProvider.class);
mVcnCallback = mock(VcnCallback.class);
@@ -97,12 +122,15 @@ public class VcnTest {
doReturn(mContext).when(mVcnContext).getContext();
doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
+ doReturn(mContentResolver).when(mDeps).newVcnContentResolver(eq(mVcnContext));
// Setup VcnGatewayConnection instance generation
doAnswer((invocation) -> {
// Mock-within a doAnswer is safe, because it doesn't actually run nested.
return mock(VcnGatewayConnection.class);
- }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any(), any());
+ }).when(mDeps).newVcnGatewayConnection(any(), any(), any(), any(), any(), anyBoolean());
+
+ doReturn(TEST_SUB_IDS_IN_GROUP).when(mSubscriptionSnapshot).getAllSubIdsInGroup(any());
mGatewayStatusCallbackCaptor = ArgumentCaptor.forClass(VcnGatewayStatusCallback.class);
@@ -123,6 +151,16 @@ public class VcnTest {
mDeps);
}
+ private TelephonyManager setupAndGetTelephonyManager(boolean isMobileDataEnabled) {
+ final TelephonyManager telephonyManager = mock(TelephonyManager.class);
+ VcnTestUtils.setupSystemService(
+ mContext, telephonyManager, Context.TELEPHONY_SERVICE, TelephonyManager.class);
+ doReturn(telephonyManager).when(telephonyManager).createForSubscriptionId(anyInt());
+ doReturn(isMobileDataEnabled).when(telephonyManager).isDataEnabled();
+
+ return telephonyManager;
+ }
+
private NetworkRequestListener verifyAndGetRequestListener() {
ArgumentCaptor<NetworkRequestListener> mNetworkRequestListenerCaptor =
ArgumentCaptor.forClass(NetworkRequestListener.class);
@@ -164,6 +202,39 @@ public class VcnTest {
}
@Test
+ public void testContentObserverRegistered() {
+ // Validate state from setUp()
+ final Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA);
+ verify(mContentResolver)
+ .registerContentObserver(eq(uri), eq(true), any(ContentObserver.class));
+ }
+
+ @Test
+ public void testMobileDataStateCheckedOnInitialization_enabled() {
+ // Validate state from setUp()
+ assertTrue(mVcn.isMobileDataEnabled());
+ verify(mTelephonyManager).isDataEnabled();
+ }
+
+ @Test
+ public void testMobileDataStateCheckedOnInitialization_disabled() {
+ // Build and setup new telephonyManager to ensure method call count is reset.
+ final TelephonyManager telephonyManager =
+ setupAndGetTelephonyManager(false /* isMobileDataEnabled */);
+ final Vcn vcn =
+ new Vcn(
+ mVcnContext,
+ TEST_SUB_GROUP,
+ mConfig,
+ mSubscriptionSnapshot,
+ mVcnCallback,
+ mDeps);
+
+ assertFalse(vcn.isMobileDataEnabled());
+ verify(mTelephonyManager).isDataEnabled();
+ }
+
+ @Test
public void testSubscriptionSnapshotUpdatesVcnGatewayConnections() {
verifyUpdateSubscriptionSnapshotNotifiesGatewayConnections(VCN_STATUS_CODE_ACTIVE);
}
@@ -193,7 +264,8 @@ public class VcnTest {
eq(TEST_SUB_GROUP),
eq(mSubscriptionSnapshot),
any(),
- mGatewayStatusCallbackCaptor.capture());
+ mGatewayStatusCallbackCaptor.capture(),
+ eq(MOBILE_DATA_ENABLED));
return gatewayConnections;
}
@@ -256,20 +328,21 @@ public class VcnTest {
mTestLooper.dispatchAll();
// Verify that the VCN requests the networkRequests be resent
- assertEquals(1, mVcn.getVcnGatewayConnections().size());
+ assertEquals(gatewayConnections.size() - 1, mVcn.getVcnGatewayConnections().size());
verify(mVcnNetworkProvider).resendAllRequests(requestListener);
// Verify that the VcnGatewayConnection is restarted if a request exists for it
triggerVcnRequestListeners(requestListener);
mTestLooper.dispatchAll();
- assertEquals(2, mVcn.getVcnGatewayConnections().size());
+ assertEquals(gatewayConnections.size(), mVcn.getVcnGatewayConnections().size());
verify(mDeps, times(gatewayConnections.size() + 1))
.newVcnGatewayConnection(
eq(mVcnContext),
eq(TEST_SUB_GROUP),
eq(mSubscriptionSnapshot),
any(),
- mGatewayStatusCallbackCaptor.capture());
+ mGatewayStatusCallbackCaptor.capture(),
+ anyBoolean());
}
@Test
@@ -286,7 +359,7 @@ public class VcnTest {
public void testUpdateConfigReevaluatesGatewayConnections() {
final NetworkRequestListener requestListener = verifyAndGetRequestListener();
startGatewaysAndGetGatewayConnections(requestListener);
- assertEquals(2, mVcn.getVcnGatewayConnectionConfigMap().size());
+ assertEquals(TEST_CAPS.length, mVcn.getVcnGatewayConnectionConfigMap().size());
// Create VcnConfig with only one VcnGatewayConnectionConfig so a gateway connection is torn
// down. Reuse existing VcnGatewayConnectionConfig so that the gateway connection name
@@ -309,4 +382,57 @@ public class VcnTest {
verify(removedGatewayConnection).teardownAsynchronously();
verify(mVcnNetworkProvider).resendAllRequests(requestListener);
}
+
+ private void verifyMobileDataToggled(boolean startingToggleState, boolean endingToggleState) {
+ final ArgumentCaptor<ContentObserver> captor =
+ ArgumentCaptor.forClass(ContentObserver.class);
+ verify(mContentResolver).registerContentObserver(any(), anyBoolean(), captor.capture());
+ final ContentObserver contentObserver = captor.getValue();
+
+ // Start VcnGatewayConnections
+ mVcn.setMobileDataEnabled(startingToggleState);
+ triggerVcnRequestListeners(verifyAndGetRequestListener());
+ final Map<VcnGatewayConnectionConfig, VcnGatewayConnection> gateways =
+ mVcn.getVcnGatewayConnectionConfigMap();
+
+ // Trigger data toggle change.
+ doReturn(endingToggleState).when(mTelephonyManager).isDataEnabled();
+ contentObserver.onChange(false /* selfChange, ignored */);
+ mTestLooper.dispatchAll();
+
+ // Verify that data toggle changes restart ONLY INTERNET or DUN networks, and only if the
+ // toggle state changed.
+ for (Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry : gateways.entrySet()) {
+ final Set<Integer> exposedCaps = entry.getKey().getAllExposedCapabilities();
+ if (startingToggleState != endingToggleState
+ && (exposedCaps.contains(NET_CAPABILITY_INTERNET)
+ || exposedCaps.contains(NET_CAPABILITY_DUN))) {
+ verify(entry.getValue()).teardownAsynchronously();
+ } else {
+ verify(entry.getValue(), never()).teardownAsynchronously();
+ }
+ }
+
+ assertEquals(endingToggleState, mVcn.isMobileDataEnabled());
+ }
+
+ @Test
+ public void testMobileDataEnabled() {
+ verifyMobileDataToggled(false /* startingToggleState */, true /* endingToggleState */);
+ }
+
+ @Test
+ public void testMobileDataDisabled() {
+ verifyMobileDataToggled(true /* startingToggleState */, false /* endingToggleState */);
+ }
+
+ @Test
+ public void testMobileDataObserverFiredWithoutChanges_dataEnabled() {
+ verifyMobileDataToggled(false /* startingToggleState */, false /* endingToggleState */);
+ }
+
+ @Test
+ public void testMobileDataObserverFiredWithoutChanges_dataDisabled() {
+ verifyMobileDataToggled(true /* startingToggleState */, true /* endingToggleState */);
+ }
}