diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 8562b0d9cb828..6a72010738db2 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -27,10 +27,12 @@ import android.content.Context; import android.net.ConnectivityManager; import android.net.LinkProperties; import android.net.NetworkCapabilities; +import android.net.TelephonyNetworkSpecifier; import android.net.vcn.IVcnManagementService; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; import android.net.vcn.VcnUnderlyingNetworkPolicy; +import android.net.wifi.WifiInfo; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; @@ -291,6 +293,12 @@ public class VcnManagementService extends IVcnManagementService.Stub { @NonNull VcnConfig config) { return new Vcn(vcnContext, subscriptionGroup, config); } + + /** Gets the subId indicated by the given {@link WifiInfo}. */ + public int getSubIdForWifiInfo(@NonNull WifiInfo wifiInfo) { + // TODO(b/178501049): use the subId indicated by WifiInfo#getSubscriptionId + return SubscriptionManager.INVALID_SUBSCRIPTION_ID; + } } /** Notifies the VcnManagementService that external dependencies can be set up. */ @@ -582,8 +590,36 @@ public class VcnManagementService extends IVcnManagementService.Stub { "Must have permission NETWORK_FACTORY or be the SystemServer to get underlying" + " Network policies"); - // TODO(b/175914059): implement policy generation once VcnManagementService is able to - // determine policies + // Defensive copy in case this call is in-process and the given NetworkCapabilities mutates + networkCapabilities = new NetworkCapabilities(networkCapabilities); + + int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) + && networkCapabilities.getNetworkSpecifier() instanceof TelephonyNetworkSpecifier) { + TelephonyNetworkSpecifier telephonyNetworkSpecifier = + (TelephonyNetworkSpecifier) networkCapabilities.getNetworkSpecifier(); + subId = telephonyNetworkSpecifier.getSubscriptionId(); + } else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) + && networkCapabilities.getTransportInfo() instanceof WifiInfo) { + WifiInfo wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo(); + subId = mDeps.getSubIdForWifiInfo(wifiInfo); + } + + boolean isVcnManagedNetwork = false; + if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + synchronized (mLock) { + ParcelUuid subGroup = mLastSnapshot.getGroupForSubId(subId); + + // TODO(b/178140910): only mark the Network as VCN-managed if not in safe mode + if (mVcns.containsKey(subGroup)) { + isVcnManagedNetwork = true; + } + } + } + if (isVcnManagedNetwork) { + networkCapabilities.removeCapability( + NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED); + } return new VcnUnderlyingNetworkPolicy(false /* isTearDownRequested */, networkCapabilities); } diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index e26bf19488d03..e7d334ebd490e 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -22,7 +22,6 @@ import static com.android.server.vcn.VcnTestUtils.setupSystemService; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -30,6 +29,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.argThat; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; @@ -39,16 +39,20 @@ import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.content.Context; import android.net.ConnectivityManager; import android.net.LinkProperties; import android.net.NetworkCapabilities; +import android.net.NetworkCapabilities.Transport; +import android.net.TelephonyNetworkSpecifier; import android.net.vcn.IVcnUnderlyingNetworkPolicyListener; import android.net.vcn.VcnConfig; import android.net.vcn.VcnConfigTest; import android.net.vcn.VcnUnderlyingNetworkPolicy; +import android.net.wifi.WifiInfo; import android.os.IBinder; import android.os.ParcelUuid; import android.os.PersistableBundle; @@ -253,7 +257,14 @@ public class VcnManagementServiceTest { verify(mConfigReadWriteHelper).readFromDisk(); } - private void triggerSubscriptionTrackerCallback(Set activeSubscriptionGroups) { + private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot( + Set activeSubscriptionGroups) { + return triggerSubscriptionTrackerCbAndGetSnapshot( + activeSubscriptionGroups, Collections.emptyMap()); + } + + private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot( + Set activeSubscriptionGroups, Map subIdToGroupMap) { final TelephonySubscriptionSnapshot snapshot = mock(TelephonySubscriptionSnapshot.class); doReturn(activeSubscriptionGroups).when(snapshot).getActiveSubscriptionGroups(); @@ -267,8 +278,14 @@ public class VcnManagementServiceTest { argThat(val -> activeSubscriptionGroups.contains(val)), eq(TEST_PACKAGE_NAME)); + doAnswer(invocation -> { + return subIdToGroupMap.get(invocation.getArgument(0)); + }).when(snapshot).getGroupForSubId(anyInt()); + final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback(); cb.onNewSnapshot(snapshot); + + return snapshot; } private TelephonySubscriptionTrackerCallback getTelephonySubscriptionTrackerCallback() { @@ -287,7 +304,7 @@ public class VcnManagementServiceTest { @Test public void testTelephonyNetworkTrackerCallbackStartsInstances() throws Exception { - triggerSubscriptionTrackerCallback(Collections.singleton(TEST_UUID_1)); + triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1)); verify(mMockDeps).newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG)); } @@ -296,7 +313,7 @@ public class VcnManagementServiceTest { final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback(); final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2); - triggerSubscriptionTrackerCallback(Collections.emptySet()); + triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet()); // Verify teardown after delay mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS); @@ -311,13 +328,13 @@ public class VcnManagementServiceTest { final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2); // Simulate SIM unloaded - triggerSubscriptionTrackerCallback(Collections.emptySet()); + triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet()); // Simulate new SIM loaded right during teardown delay. mTestLooper.moveTimeForward( VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2); mTestLooper.dispatchAll(); - triggerSubscriptionTrackerCallback(Collections.singleton(TEST_UUID_2)); + triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2)); // Verify that even after the full timeout duration, the VCN instance is not torn down mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS); @@ -331,7 +348,7 @@ public class VcnManagementServiceTest { final Vcn oldInstance = startAndGetVcnInstance(TEST_UUID_2); // Simulate SIM unloaded - triggerSubscriptionTrackerCallback(Collections.emptySet()); + triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet()); // Config cleared, SIM reloaded & config re-added right before teardown delay, staring new // vcnInstance. @@ -496,14 +513,73 @@ public class VcnManagementServiceTest { mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); } + private void setUpVcnSubscription(int subId, ParcelUuid subGroup) { + mVcnMgmtSvc.setVcnConfig(subGroup, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); + + triggerSubscriptionTrackerCbAndGetSnapshot( + Collections.singleton(subGroup), Collections.singletonMap(subId, subGroup)); + } + + private void verifyMergedNetworkCapabilities( + NetworkCapabilities mergedCapabilities, @Transport int transportType) { + assertTrue(mergedCapabilities.hasTransport(transportType)); + assertFalse( + mergedCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)); + } + @Test - public void testGetUnderlyingNetworkPolicy() throws Exception { + public void testGetUnderlyingNetworkPolicyTransportCell() throws Exception { + setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2); + + NetworkCapabilities nc = + new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID)) + .build(); + VcnUnderlyingNetworkPolicy policy = - mVcnMgmtSvc.getUnderlyingNetworkPolicy( - new NetworkCapabilities(), new LinkProperties()); + mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties()); assertFalse(policy.isTeardownRequested()); - assertNotNull(policy.getMergedNetworkCapabilities()); + verifyMergedNetworkCapabilities( + policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_CELLULAR); + } + + @Test + public void testGetUnderlyingNetworkPolicyTransportWifi() throws Exception { + setUpVcnSubscription(TEST_SUBSCRIPTION_ID, TEST_UUID_2); + + WifiInfo wifiInfo = mock(WifiInfo.class); + when(wifiInfo.makeCopy(anyBoolean())).thenReturn(wifiInfo); + when(mMockDeps.getSubIdForWifiInfo(eq(wifiInfo))).thenReturn(TEST_SUBSCRIPTION_ID); + NetworkCapabilities nc = + new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .setTransportInfo(wifiInfo) + .build(); + + VcnUnderlyingNetworkPolicy policy = + mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties()); + + assertFalse(policy.isTeardownRequested()); + verifyMergedNetworkCapabilities( + policy.getMergedNetworkCapabilities(), NetworkCapabilities.TRANSPORT_WIFI); + } + + @Test + public void testGetUnderlyingNetworkPolicyNonVcnNetwork() throws Exception { + NetworkCapabilities nc = + new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .setNetworkSpecifier(new TelephonyNetworkSpecifier(TEST_SUBSCRIPTION_ID)) + .build(); + + VcnUnderlyingNetworkPolicy policy = + mVcnMgmtSvc.getUnderlyingNetworkPolicy(nc, new LinkProperties()); + + assertFalse(policy.isTeardownRequested()); + assertEquals(nc, policy.getMergedNetworkCapabilities()); } @Test(expected = SecurityException.class)