diff options
author | Scott Lobdell <slobdell@google.com> | 2021-07-27 17:02:32 +0000 |
---|---|---|
committer | Scott Lobdell <slobdell@google.com> | 2021-07-27 17:02:32 +0000 |
commit | cb84bc77bfeb89a940d8439f7458fe5d9bef7bef (patch) | |
tree | d6e70908803e918eb485e058341ce55d0a957188 /tests | |
parent | dc5ea9d31ab76ba378da9c550813e6b7d8be1e69 (diff) | |
parent | 6aa393b52cd7362100a2b3e9b0b1dece473cf6dd (diff) |
Merge SP1A.210723.002
Change-Id: I220cdfc5cb9db40162fd33f400a54591018d54cf
Diffstat (limited to 'tests')
11 files changed, 560 insertions, 32 deletions
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp index 1aa04996f682..cac14a72a706 100644 --- a/tests/StagedInstallTest/Android.bp +++ b/tests/StagedInstallTest/Android.bp @@ -32,6 +32,7 @@ android_test_helper_app { test_suites: ["general-tests"], java_resources: [ ":com.android.apex.apkrollback.test_v2", + ":StagedInstallTestApexV2", ":StagedInstallTestApexV2_WrongSha", ":test.rebootless_apex_v1", ":test.rebootless_apex_v2", diff --git a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java index 9cdaef75c491..4684f0182d03 100644 --- a/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java +++ b/tests/StagedInstallTest/app/src/com/android/tests/stagedinstallinternal/StagedInstallInternalTest.java @@ -55,8 +55,11 @@ public class StagedInstallInternalTest { private static final TestApp TEST_APEX_WITH_APK_V2 = new TestApp("TestApexWithApkV2", APK_IN_APEX_TESTAPEX_NAME, 2, /*isApex*/true, APK_IN_APEX_TESTAPEX_NAME + "_v2.apex"); private static final TestApp APEX_WRONG_SHA_V2 = new TestApp( - "ApexWrongSha2", SHIM_APEX_PACKAGE_NAME, 2, /*isApex*/true, + "ApexWrongSha2", SHIM_APEX_PACKAGE_NAME, 2, /* isApex= */ true, "com.android.apex.cts.shim.v2_wrong_sha.apex"); + private static final TestApp APEX_V2 = new TestApp( + "ApexV2", SHIM_APEX_PACKAGE_NAME, 2, /* isApex= */ true, + "com.android.apex.cts.shim.v2.apex"); private File mTestStateFile = new File( InstrumentationRegistry.getInstrumentation().getContext().getFilesDir(), @@ -196,6 +199,137 @@ public class StagedInstallInternalTest { } @Test + public void testActiveApexIsRevertedOnCheckpointRollback_Prepare() throws Exception { + int sessionId = Install.single(TestApp.Apex2).setStaged().commit(); + assertSessionReady(sessionId); + storeSessionId(sessionId); + } + + @Test + public void testActiveApexIsRevertedOnCheckpointRollback_Commit() throws Exception { + // Verify apex installed during preparation was successful + int sessionId = retrieveLastSessionId(); + assertSessionApplied(sessionId); + assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); + // Commit a new staged session + sessionId = Install.single(TestApp.Apex3).setStaged().commit(); + assertSessionReady(sessionId); + storeSessionId(sessionId); + } + + @Test + public void testActiveApexIsRevertedOnCheckpointRollback_VerifyPostReboot() throws Exception { + int sessionId = retrieveLastSessionId(); + assertSessionFailed(sessionId); + assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(2); + } + + @Test + public void testApexIsNotActivatedIfNotInCheckpointMode_Commit() throws Exception { + int sessionId = Install.single(TestApp.Apex2).setStaged().commit(); + assertSessionReady(sessionId); + storeSessionId(sessionId); + assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); + } + + @Test + public void testApexIsNotActivatedIfNotInCheckpointMode_VerifyPostReboot() throws Exception { + int sessionId = retrieveLastSessionId(); + assertSessionFailed(sessionId); + assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); + } + + @Test + public void testApexInstallerNotInAllowListCanNotInstall_staged() throws Exception { + assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); + // We don't really care which APEX we are trying to install here, since the session creation + // should fail immediately. + InstallUtils.commitExpectingFailure( + SecurityException.class, + "Installer not allowed to commit staged install", + Install.single(APEX_WRONG_SHA_V2).setBypassStangedInstallerCheck(false) + .setStaged()); + } + + @Test + public void testApexInstallerNotInAllowListCanNotInstall_nonStaged() throws Exception { + assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); + // We don't really care which APEX we are trying to install here, since the session creation + // should fail immediately. + InstallUtils.commitExpectingFailure( + SecurityException.class, + "Installer not allowed to commit non-staged APEX install", + Install.single(APEX_WRONG_SHA_V2).setBypassStangedInstallerCheck(false)); + } + + @Test + public void testApexNotInAllowListCanNotInstall_staged() throws Exception { + assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1); + TestApp apex = new TestApp("apex", "test.apex.rebootless", 2, + /* isApex= */ true, "test.rebootless_apex_v2.apex"); + InstallUtils.commitExpectingFailure( + AssertionError.class, + "Update of APEX package test.apex.rebootless is not allowed " + + "for com.android.tests.stagedinstallinternal", + Install.single(apex).setBypassAllowedApexUpdateCheck(false).setStaged()); + } + + @Test + public void testApexNotInAllowListCanNotInstall_nonStaged() throws Exception { + assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1); + TestApp apex = new TestApp("apex", "test.apex.rebootless", 2, + /* isApex= */ true, "test.rebootless_apex_v2.apex"); + InstallUtils.commitExpectingFailure( + AssertionError.class, + "Update of APEX package test.apex.rebootless is not allowed " + + "for com.android.tests.stagedinstallinternal", + Install.single(apex).setBypassAllowedApexUpdateCheck(false)); + } + + @Test + public void testVendorApexWrongInstaller_staged() throws Exception { + assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1); + TestApp apex = new TestApp("apex", "test.apex.rebootless", 2, + /* isApex= */ true, "test.rebootless_apex_v2.apex"); + InstallUtils.commitExpectingFailure( + AssertionError.class, + "Update of APEX package test.apex.rebootless is not allowed " + + "for com.android.tests.stagedinstallinternal", + Install.single(apex).setBypassAllowedApexUpdateCheck(false).setStaged()); + } + + @Test + public void testVendorApexWrongInstaller_nonStaged() throws Exception { + assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1); + TestApp apex = new TestApp("apex", "test.apex.rebootless", 2, + /* isApex= */ true, "test.rebootless_apex_v2.apex"); + InstallUtils.commitExpectingFailure( + AssertionError.class, + "Update of APEX package test.apex.rebootless is not allowed " + + "for com.android.tests.stagedinstallinternal", + Install.single(apex).setBypassAllowedApexUpdateCheck(false)); + } + + @Test + public void testVendorApexCorrectInstaller_staged() throws Exception { + assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1); + TestApp apex = new TestApp("apex", "test.apex.rebootless", 2, + /* isApex= */ true, "test.rebootless_apex_v2.apex"); + int sessionId = + Install.single(apex).setBypassAllowedApexUpdateCheck(false).setStaged().commit(); + InstallUtils.getPackageInstaller().abandonSession(sessionId); + } + + @Test + public void testVendorApexCorrectInstaller_nonStaged() throws Exception { + assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(1); + TestApp apex = new TestApp("apex", "test.apex.rebootless", 2, + /* isApex= */ true, "test.rebootless_apex_v2.apex"); + Install.single(apex).setBypassAllowedApexUpdateCheck(false).commit(); + assertThat(InstallUtils.getInstalledVersion("test.apex.rebootless")).isEqualTo(2); + } + + @Test public void testRebootlessUpdates() throws Exception { InstallUtils.dropShellPermissionIdentity(); InstallUtils.adoptShellPermissionIdentity(Manifest.permission.INSTALL_PACKAGE_UPDATES); @@ -257,6 +391,31 @@ public class StagedInstallInternalTest { } } + @Test + public void testRebootlessUpdate_hasStagedSessionWithSameApex_fails() throws Exception { + assertThat(InstallUtils.getInstalledVersion(SHIM_APEX_PACKAGE_NAME)).isEqualTo(1); + + int sessionId = Install.single(APEX_V2).setStaged().commit(); + assertSessionReady(sessionId); + InstallUtils.commitExpectingFailure( + AssertionError.class, + "Staged session " + sessionId + " already contains " + SHIM_APEX_PACKAGE_NAME, + Install.single(APEX_V2)); + + } + + private static void assertSessionApplied(int sessionId) { + assertSessionState(sessionId, (session) -> { + assertThat(session.isStagedSessionApplied()).isTrue(); + }); + } + + private static void assertSessionFailed(int sessionId) { + assertSessionState(sessionId, (session) -> { + assertThat(session.isStagedSessionFailed()).isTrue(); + }); + } + private static void assertSessionFailedWithMessage(int sessionId, String msg) { assertSessionState(sessionId, (session) -> { assertThat(session.isStagedSessionFailed()).isTrue(); diff --git a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java index e19f97b6c045..5021009f65ae 100644 --- a/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java +++ b/tests/StagedInstallTest/src/com/android/tests/stagedinstallinternal/host/StagedInstallInternalTest.java @@ -43,7 +43,9 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.BufferedWriter; import java.io.File; +import java.io.FileWriter; import java.util.List; import java.util.stream.Collectors; @@ -60,6 +62,9 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { private static final String APK_A = "TestAppAv1.apk"; private static final String APK_IN_APEX_TESTAPEX_NAME = "com.android.apex.apkrollback.test"; + private static final String TEST_VENDOR_APEX_ALLOW_LIST = + "/vendor/etc/sysconfig/test-vendor-apex-allow-list.xml"; + private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this); /** @@ -87,7 +92,8 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { "/data/apex/active/" + APK_IN_APEX_TESTAPEX_NAME + "*.apex", "/data/apex/active/" + SHIM_APEX_PACKAGE_NAME + "*.apex", "/system/apex/test.rebootless_apex_v1.apex", - "/data/apex/active/test.apex.rebootless*.apex"); + "/data/apex/active/test.apex.rebootless*.apex", + TEST_VENDOR_APEX_ALLOW_LIST); } @Before @@ -134,7 +140,23 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { } getDevice().remountSystemWritable(); assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName)); - getDevice().reboot(); + } + + private void pushTestVendorApexAllowList(String installerPackageName) throws Exception { + if (!getDevice().isAdbRoot()) { + getDevice().enableAdbRoot(); + } + getDevice().remountSystemWritable(); + File file = File.createTempFile("test-vendor-apex-allow-list", ".xml"); + try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) { + final String fmt = + "<config>\n" + + " <allowed-vendor-apex package=\"test.apex.rebootless\" " + + " installerPackage=\"%s\" />\n" + + "</config>"; + writer.write(String.format(fmt, installerPackageName)); + } + getDevice().pushFile(file, TEST_VENDOR_APEX_ALLOW_LIST); } /** @@ -144,6 +166,8 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { @LargeTest public void testDuplicateApkInApexShouldFail() throws Exception { pushTestApex(APK_IN_APEX_TESTAPEX_NAME + "_v1.apex"); + getDevice().reboot(); + runPhase("testDuplicateApkInApexShouldFail_Commit"); getDevice().reboot(); runPhase("testDuplicateApkInApexShouldFail_Verify"); @@ -346,11 +370,114 @@ public class StagedInstallInternalTest extends BaseHostJUnit4Test { } @Test + public void testActiveApexIsRevertedOnCheckpointRollback() throws Exception { + assumeTrue("Device does not support updating APEX", + mHostUtils.isApexUpdateSupported()); + assumeTrue("Device does not support file-system checkpoint", + mHostUtils.isCheckpointSupported()); + + // Install something so that /data/apex/active is not empty + runPhase("testActiveApexIsRevertedOnCheckpointRollback_Prepare"); + getDevice().reboot(); + + // Stage another session which will be installed during fs-rollback mode + runPhase("testActiveApexIsRevertedOnCheckpointRollback_Commit"); + + // Set checkpoint to 0 so that we enter fs-rollback mode immediately on reboot + getDevice().enableAdbRoot(); + getDevice().executeShellCommand("vdc checkpoint startCheckpoint 0"); + getDevice().disableAdbRoot(); + getDevice().reboot(); + + // Verify that session was reverted and we have fallen back to + // apex installed during preparation stage. + runPhase("testActiveApexIsRevertedOnCheckpointRollback_VerifyPostReboot"); + } + + @Test + public void testApexIsNotActivatedIfNotInCheckpointMode() throws Exception { + assumeTrue("Device does not support updating APEX", + mHostUtils.isApexUpdateSupported()); + assumeTrue("Device does not support file-system checkpoint", + mHostUtils.isCheckpointSupported()); + + runPhase("testApexIsNotActivatedIfNotInCheckpointMode_Commit"); + // Delete checkpoint file in /metadata so that device thinks + // fs-checkpointing was never activated + getDevice().enableAdbRoot(); + getDevice().executeShellCommand("rm /metadata/vold/checkpoint"); + getDevice().disableAdbRoot(); + getDevice().reboot(); + // Verify that session was not installed when not in fs-checkpoint mode + runPhase("testApexIsNotActivatedIfNotInCheckpointMode_VerifyPostReboot"); + } + + @Test + public void testApexInstallerNotInAllowListCanNotInstall() throws Exception { + assumeTrue("Device does not support updating APEX", + mHostUtils.isApexUpdateSupported()); + + runPhase("testApexInstallerNotInAllowListCanNotInstall_staged"); + runPhase("testApexInstallerNotInAllowListCanNotInstall_nonStaged"); + } + + @Test + @LargeTest + public void testApexNotInAllowListCanNotInstall() throws Exception { + assumeTrue("Device does not support updating APEX", + mHostUtils.isApexUpdateSupported()); + + pushTestApex("test.rebootless_apex_v1.apex"); + getDevice().reboot(); + + runPhase("testApexNotInAllowListCanNotInstall_staged"); + runPhase("testApexNotInAllowListCanNotInstall_nonStaged"); + } + + @Test + @LargeTest + public void testVendorApexWrongInstaller() throws Exception { + assumeTrue("Device does not support updating APEX", + mHostUtils.isApexUpdateSupported()); + + pushTestVendorApexAllowList("com.wrong.installer"); + pushTestApex("test.rebootless_apex_v1.apex"); + getDevice().reboot(); + + runPhase("testVendorApexWrongInstaller_staged"); + runPhase("testVendorApexWrongInstaller_nonStaged"); + } + + @Test + @LargeTest + public void testVendorApexCorrectInstaller() throws Exception { + assumeTrue("Device does not support updating APEX", + mHostUtils.isApexUpdateSupported()); + + pushTestVendorApexAllowList("com.android.tests.stagedinstallinternal"); + pushTestApex("test.rebootless_apex_v1.apex"); + getDevice().reboot(); + + runPhase("testVendorApexCorrectInstaller_staged"); + runPhase("testVendorApexCorrectInstaller_nonStaged"); + } + + @Test public void testRebootlessUpdates() throws Exception { pushTestApex("test.rebootless_apex_v1.apex"); + getDevice().reboot(); + runPhase("testRebootlessUpdates"); } + @Test + public void testRebootlessUpdate_hasStagedSessionWithSameApex_fails() throws Exception { + assumeTrue("Device does not support updating APEX", + mHostUtils.isApexUpdateSupported()); + + runPhase("testRebootlessUpdate_hasStagedSessionWithSameApex_fails"); + } + private List<String> getStagingDirectories() throws DeviceNotAvailableException { String baseDir = "/data/app-staging"; try { diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index b7a6d0ff7607..7c7dc4d79e9a 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -23,6 +23,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE; import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE; +import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS; @@ -50,6 +51,7 @@ import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.annotation.NonNull; @@ -99,6 +101,7 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import java.io.FileNotFoundException; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -227,6 +230,7 @@ public class VcnManagementServiceTest { setupMockedCarrierPrivilege(true); mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps); + setupActiveSubscription(TEST_UUID_1); doReturn(mMockIBinder).when(mMockPolicyListener).asBinder(); doReturn(mMockIBinder).when(mMockStatusCallback).asBinder(); @@ -300,23 +304,65 @@ public class VcnManagementServiceTest { } private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot( - Set<ParcelUuid> activeSubscriptionGroups) { + ParcelUuid activeDataSubGrp, Set<ParcelUuid> activeSubscriptionGroups) { return triggerSubscriptionTrackerCbAndGetSnapshot( - activeSubscriptionGroups, Collections.emptyMap()); + activeDataSubGrp, activeSubscriptionGroups, Collections.emptyMap()); } private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot( - Set<ParcelUuid> activeSubscriptionGroups, Map<Integer, ParcelUuid> subIdToGroupMap) { + ParcelUuid activeDataSubGrp, + Set<ParcelUuid> activeSubscriptionGroups, + Map<Integer, ParcelUuid> subIdToGroupMap) { + return triggerSubscriptionTrackerCbAndGetSnapshot( + activeDataSubGrp, + activeSubscriptionGroups, + subIdToGroupMap, + true /* hasCarrierPrivileges */); + } + + private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot( + ParcelUuid activeDataSubGrp, + Set<ParcelUuid> activeSubscriptionGroups, + Map<Integer, ParcelUuid> subIdToGroupMap, + boolean hasCarrierPrivileges) { return triggerSubscriptionTrackerCbAndGetSnapshot( - activeSubscriptionGroups, subIdToGroupMap, true /* hasCarrierPrivileges */); + TEST_SUBSCRIPTION_ID, + activeDataSubGrp, + activeSubscriptionGroups, + subIdToGroupMap, + hasCarrierPrivileges); } private TelephonySubscriptionSnapshot triggerSubscriptionTrackerCbAndGetSnapshot( + int activeDataSubId, + ParcelUuid activeDataSubGrp, + Set<ParcelUuid> activeSubscriptionGroups, + Map<Integer, ParcelUuid> subIdToGroupMap, + boolean hasCarrierPrivileges) { + final TelephonySubscriptionSnapshot snapshot = + buildSubscriptionSnapshot( + activeDataSubId, + activeDataSubGrp, + activeSubscriptionGroups, + subIdToGroupMap, + hasCarrierPrivileges); + + final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback(); + cb.onNewSnapshot(snapshot); + + return snapshot; + } + + private TelephonySubscriptionSnapshot buildSubscriptionSnapshot( + int activeDataSubId, + ParcelUuid activeDataSubGrp, Set<ParcelUuid> activeSubscriptionGroups, Map<Integer, ParcelUuid> subIdToGroupMap, boolean hasCarrierPrivileges) { final TelephonySubscriptionSnapshot snapshot = mock(TelephonySubscriptionSnapshot.class); doReturn(activeSubscriptionGroups).when(snapshot).getActiveSubscriptionGroups(); + doReturn(activeDataSubGrp).when(snapshot).getActiveDataSubscriptionGroup(); + doReturn(activeDataSubId).when(snapshot).getActiveDataSubscriptionId(); final Set<String> privilegedPackages = (activeSubscriptionGroups == null || activeSubscriptionGroups.isEmpty()) @@ -343,12 +389,19 @@ public class VcnManagementServiceTest { return subIds; }).when(snapshot).getAllSubIdsInGroup(any()); - final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback(); - cb.onNewSnapshot(snapshot); - return snapshot; } + private void setupActiveSubscription(ParcelUuid activeDataSubGrp) { + mVcnMgmtSvc.setLastSnapshot( + buildSubscriptionSnapshot( + TEST_SUBSCRIPTION_ID, + activeDataSubGrp, + Collections.emptySet(), + Collections.emptyMap(), + true /* hasCarrierPrivileges */)); + } + private TelephonySubscriptionTrackerCallback getTelephonySubscriptionTrackerCallback() { final ArgumentCaptor<TelephonySubscriptionTrackerCallback> captor = ArgumentCaptor.forClass(TelephonySubscriptionTrackerCallback.class); @@ -372,25 +425,56 @@ public class VcnManagementServiceTest { @Test public void testTelephonyNetworkTrackerCallbackStartsInstances() throws Exception { + // Add a record for a non-active SIM + mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); + TelephonySubscriptionSnapshot snapshot = - triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1)); + triggerSubscriptionTrackerCbAndGetSnapshot( + TEST_UUID_1, new ArraySet<>(Arrays.asList(TEST_UUID_1, TEST_UUID_2))); verify(mMockDeps) .newVcnContext( eq(mMockContext), eq(mTestLooper.getLooper()), any(VcnNetworkProvider.class), anyBoolean()); + + // Verify that only the VCN for the active data SIM was started. verify(mMockDeps) .newVcn(eq(mVcnContext), eq(TEST_UUID_1), eq(TEST_VCN_CONFIG), eq(snapshot), any()); + verify(mMockDeps, never()) + .newVcn(eq(mVcnContext), eq(TEST_UUID_2), eq(TEST_VCN_CONFIG), eq(snapshot), any()); + } + + @Test + public void testTelephonyNetworkTrackerCallbackSwitchingActiveDataStartsAndStopsInstances() + throws Exception { + // Add a record for a non-active SIM + mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); + final Vcn vcn = startAndGetVcnInstance(TEST_UUID_1); + + TelephonySubscriptionSnapshot snapshot = + triggerSubscriptionTrackerCbAndGetSnapshot( + TEST_UUID_2, new ArraySet<>(Arrays.asList(TEST_UUID_1, TEST_UUID_2))); + + // Verify that a new VCN for UUID_2 was started, and the old instance was torn down + // immediately + verify(mMockDeps) + .newVcn(eq(mVcnContext), eq(TEST_UUID_2), eq(TEST_VCN_CONFIG), eq(snapshot), any()); + verify(vcn).teardownAsynchronously(); + assertEquals(1, mVcnMgmtSvc.getAllVcns().size()); + assertFalse(mVcnMgmtSvc.getAllVcns().containsKey(TEST_UUID_1)); + assertTrue(mVcnMgmtSvc.getAllVcns().containsKey(TEST_UUID_2)); } @Test public void testTelephonyNetworkTrackerCallbackStopsInstances() throws Exception { + setupActiveSubscription(TEST_UUID_2); + final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback(); final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2); mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); - triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet()); + triggerSubscriptionTrackerCbAndGetSnapshot(null, Collections.emptySet()); // Verify teardown after delay mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS); @@ -400,19 +484,76 @@ public class VcnManagementServiceTest { } @Test + public void testTelephonyNetworkTrackerCallbackSwitchToNewSubscriptionImmediatelyTearsDown() + throws Exception { + setupActiveSubscription(TEST_UUID_2); + + final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback(); + final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2); + + // Simulate switch to different default data subscription that does not have a VCN. + triggerSubscriptionTrackerCbAndGetSnapshot( + TEST_SUBSCRIPTION_ID, + null /* activeDataSubscriptionGroup */, + Collections.emptySet(), + Collections.emptyMap(), + false /* hasCarrierPrivileges */); + mTestLooper.dispatchAll(); + + verify(vcn).teardownAsynchronously(); + assertEquals(0, mVcnMgmtSvc.getAllVcns().size()); + } + + /** + * Tests an intermediate state where carrier privileges are marked as lost before active data + * subId changes during a SIM ejection. + * + * <p>The expected outcome is that the VCN is torn down after a delay, as opposed to + * immediately. + */ + @Test + public void testTelephonyNetworkTrackerCallbackLostCarrierPrivilegesBeforeActiveDataSubChanges() + throws Exception { + setupActiveSubscription(TEST_UUID_2); + + final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback(); + final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2); + + // Simulate privileges lost + triggerSubscriptionTrackerCbAndGetSnapshot( + TEST_SUBSCRIPTION_ID, + TEST_UUID_2, + Collections.emptySet(), + Collections.emptyMap(), + false /* hasCarrierPrivileges */); + + // Verify teardown after delay + mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS); + mTestLooper.dispatchAll(); + verify(vcn).teardownAsynchronously(); + } + + @Test public void testTelephonyNetworkTrackerCallbackSimSwitchesDoNotKillVcnInstances() throws Exception { + setupActiveSubscription(TEST_UUID_2); + final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback(); final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2); // Simulate SIM unloaded - triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet()); + triggerSubscriptionTrackerCbAndGetSnapshot( + INVALID_SUBSCRIPTION_ID, + null /* activeDataSubscriptionGroup */, + Collections.emptySet(), + Collections.emptyMap(), + false /* hasCarrierPrivileges */); // Simulate new SIM loaded right during teardown delay. mTestLooper.moveTimeForward( VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2); mTestLooper.dispatchAll(); - triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2)); + triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_2, 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); @@ -422,11 +563,13 @@ public class VcnManagementServiceTest { @Test public void testTelephonyNetworkTrackerCallbackDoesNotKillNewVcnInstances() throws Exception { + setupActiveSubscription(TEST_UUID_2); + final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback(); final Vcn oldInstance = startAndGetVcnInstance(TEST_UUID_2); // Simulate SIM unloaded - triggerSubscriptionTrackerCbAndGetSnapshot(Collections.emptySet()); + triggerSubscriptionTrackerCbAndGetSnapshot(null, Collections.emptySet()); // Config cleared, SIM reloaded & config re-added right before teardown delay, staring new // vcnInstance. @@ -434,6 +577,7 @@ public class VcnManagementServiceTest { VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2); mTestLooper.dispatchAll(); mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2, TEST_PACKAGE_NAME); + triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_2, Collections.singleton(TEST_UUID_2)); final Vcn newInstance = startAndGetVcnInstance(TEST_UUID_2); // Verify that new instance was different, and the old one was torn down @@ -538,6 +682,31 @@ public class VcnManagementServiceTest { } @Test + public void testSetVcnConfigNonActiveSimDoesNotStartVcn() throws Exception { + // Use a different UUID to simulate a new VCN config. + mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); + assertEquals(TEST_VCN_CONFIG, mVcnMgmtSvc.getConfigs().get(TEST_UUID_2)); + verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class)); + + verify(mMockDeps, never()).newVcn(any(), any(), any(), any(), any()); + } + + @Test + public void testSetVcnConfigActiveSimTearsDownExistingVcnsImmediately() throws Exception { + final Vcn vcn = startAndGetVcnInstance(TEST_UUID_1); + + // Use a different UUID to simulate a new VCN config. + setupActiveSubscription(TEST_UUID_2); + mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); + + verify(mMockDeps, times(2)).newVcn(any(), any(), any(), any(), any()); + verify(vcn).teardownAsynchronously(); + assertEquals(1, mVcnMgmtSvc.getAllVcns().size()); + assertFalse(mVcnMgmtSvc.getAllVcns().containsKey(TEST_UUID_1)); + assertTrue(mVcnMgmtSvc.getAllVcns().containsKey(TEST_UUID_2)); + } + + @Test public void testSetVcnConfigTestModeRequiresPermission() throws Exception { doThrow(new SecurityException("Requires MANAGE_TEST_NETWORKS")) .when(mMockContext) @@ -561,7 +730,7 @@ public class VcnManagementServiceTest { @Test public void testSetVcnConfigNotifiesStatusCallback() throws Exception { - triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2)); + triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_2, Collections.singleton(TEST_UUID_2)); mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_2, mMockStatusCallback, TEST_PACKAGE_NAME); verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED); @@ -635,7 +804,9 @@ public class VcnManagementServiceTest { } @Test - public void testSetVcnConfigClearVcnConfigStartsUpdatesAndTeardsDownVcns() throws Exception { + public void testSetVcnConfigClearVcnConfigStartsUpdatesAndTearsDownVcns() throws Exception { + setupActiveSubscription(TEST_UUID_2); + // Use a different UUID to simulate a new VCN config. mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); final Map<ParcelUuid, Vcn> vcnInstances = mVcnMgmtSvc.getAllVcns(); @@ -646,12 +817,7 @@ public class VcnManagementServiceTest { // Verify Vcn is started verify(mMockDeps) - .newVcn( - eq(mVcnContext), - eq(TEST_UUID_2), - eq(TEST_VCN_CONFIG), - eq(TelephonySubscriptionSnapshot.EMPTY_SNAPSHOT), - any()); + .newVcn(eq(mVcnContext), eq(TEST_UUID_2), eq(TEST_VCN_CONFIG), any(), any()); // Verify Vcn is updated if it was previously started mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); @@ -693,7 +859,7 @@ public class VcnManagementServiceTest { // Assert that if both UUID 1 and 2 are provisioned, the caller only gets ones that they are // privileged for. - triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1)); + triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_1, Collections.singleton(TEST_UUID_1)); final List<ParcelUuid> subGrps = mVcnMgmtSvc.getConfiguredSubscriptionGroups(TEST_PACKAGE_NAME); assertEquals(Collections.singletonList(TEST_UUID_1), subGrps); @@ -760,6 +926,7 @@ public class VcnManagementServiceTest { int subId, ParcelUuid subGrp, boolean isVcnActive, boolean hasCarrierPrivileges) { mVcnMgmtSvc.systemReady(); triggerSubscriptionTrackerCbAndGetSnapshot( + subGrp, Collections.singleton(subGrp), Collections.singletonMap(subId, subGrp), hasCarrierPrivileges); @@ -927,18 +1094,23 @@ public class VcnManagementServiceTest { @Test public void testSubscriptionSnapshotUpdateNotifiesVcn() { + setupActiveSubscription(TEST_UUID_2); + mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); final Map<ParcelUuid, Vcn> vcnInstances = mVcnMgmtSvc.getAllVcns(); final Vcn vcnInstance = vcnInstances.get(TEST_UUID_2); TelephonySubscriptionSnapshot snapshot = - triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2)); + triggerSubscriptionTrackerCbAndGetSnapshot( + TEST_UUID_2, Collections.singleton(TEST_UUID_2)); verify(vcnInstance).updateSubscriptionSnapshot(eq(snapshot)); } @Test public void testAddNewVcnUpdatesPolicyListener() throws Exception { + setupActiveSubscription(TEST_UUID_2); + mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); @@ -948,6 +1120,8 @@ public class VcnManagementServiceTest { @Test public void testRemoveVcnUpdatesPolicyListener() throws Exception { + setupActiveSubscription(TEST_UUID_2); + mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME); mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); @@ -958,10 +1132,13 @@ public class VcnManagementServiceTest { @Test public void testVcnSubIdChangeUpdatesPolicyListener() throws Exception { + setupActiveSubscription(TEST_UUID_2); + startAndGetVcnInstance(TEST_UUID_2); mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); triggerSubscriptionTrackerCbAndGetSnapshot( + TEST_UUID_2, Collections.singleton(TEST_UUID_2), Collections.singletonMap(TEST_SUBSCRIPTION_ID, TEST_UUID_2)); @@ -988,7 +1165,8 @@ public class VcnManagementServiceTest { private void verifyVcnSafeModeChangesNotifiesPolicyListeners(boolean enterSafeMode) throws Exception { TelephonySubscriptionSnapshot snapshot = - triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1)); + triggerSubscriptionTrackerCbAndGetSnapshot( + TEST_UUID_1, Collections.singleton(TEST_UUID_1)); mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); @@ -1014,7 +1192,8 @@ public class VcnManagementServiceTest { boolean hasPermissionsforSubGroup) throws Exception { TelephonySubscriptionSnapshot snapshot = - triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(subGroup)); + triggerSubscriptionTrackerCbAndGetSnapshot( + subGroup, Collections.singleton(subGroup)); setupSubscriptionAndStartVcn( TEST_SUBSCRIPTION_ID, subGroup, true /* isActive */, hasPermissionsforSubGroup); @@ -1089,6 +1268,7 @@ public class VcnManagementServiceTest { // timeout so the VCN goes inactive. final TelephonySubscriptionSnapshot snapshot = triggerSubscriptionTrackerCbAndGetSnapshot( + TEST_UUID_1, Collections.singleton(TEST_UUID_1), Collections.singletonMap(TEST_SUBSCRIPTION_ID, TEST_UUID_1), false /* hasCarrierPrivileges */); diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java index ca7463884d3a..1f0df62fe72c 100644 --- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java +++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java @@ -21,6 +21,7 @@ import static android.telephony.CarrierConfigManager.EXTRA_SLOT_INDEX; import static android.telephony.CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX; import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; +import static android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener; import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback; @@ -54,6 +55,7 @@ import android.telephony.CarrierConfigManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; +import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.util.ArraySet; @@ -178,6 +180,14 @@ public class TelephonySubscriptionTrackerTest { return captor.getValue(); } + private ActiveDataSubscriptionIdListener getActiveDataSubscriptionIdListener() { + final ArgumentCaptor<TelephonyCallback> captor = + ArgumentCaptor.forClass(TelephonyCallback.class); + verify(mTelephonyManager).registerTelephonyCallback(any(), captor.capture()); + + return (ActiveDataSubscriptionIdListener) captor.getValue(); + } + private Intent buildTestBroadcastIntent(boolean hasValidSubscription) { Intent intent = new Intent(ACTION_CARRIER_CONFIG_CHANGED); intent.putExtra(EXTRA_SLOT_INDEX, TEST_SIM_SLOT_INDEX); @@ -196,7 +206,14 @@ public class TelephonySubscriptionTrackerTest { private TelephonySubscriptionSnapshot buildExpectedSnapshot( Map<Integer, SubscriptionInfo> subIdToInfoMap, Map<ParcelUuid, Set<String>> privilegedPackages) { - return new TelephonySubscriptionSnapshot(subIdToInfoMap, privilegedPackages); + return new TelephonySubscriptionSnapshot(0, subIdToInfoMap, privilegedPackages); + } + + private TelephonySubscriptionSnapshot buildExpectedSnapshot( + int activeSubId, + Map<Integer, SubscriptionInfo> subIdToInfoMap, + Map<ParcelUuid, Set<String>> privilegedPackages) { + return new TelephonySubscriptionSnapshot(activeSubId, subIdToInfoMap, privilegedPackages); } private void verifyNoActiveSubscriptions() { @@ -250,6 +267,26 @@ public class TelephonySubscriptionTrackerTest { } @Test + public void testOnSubscriptionsChangedFired_onActiveSubIdsChanged() throws Exception { + setupReadySubIds(); + setPrivilegedPackagesForMock(Collections.emptyList()); + + doReturn(TEST_SUBSCRIPTION_ID_2).when(mDeps).getActiveDataSubscriptionId(); + final ActiveDataSubscriptionIdListener listener = getActiveDataSubscriptionIdListener(); + listener.onActiveDataSubscriptionIdChanged(TEST_SUBSCRIPTION_ID_2); + mTestLooper.dispatchAll(); + + ArgumentCaptor<TelephonySubscriptionSnapshot> snapshotCaptor = + ArgumentCaptor.forClass(TelephonySubscriptionSnapshot.class); + verify(mCallback).onNewSnapshot(snapshotCaptor.capture()); + + TelephonySubscriptionSnapshot snapshot = snapshotCaptor.getValue(); + assertNotNull(snapshot); + assertEquals(TEST_SUBSCRIPTION_ID_2, snapshot.getActiveDataSubscriptionId()); + assertEquals(TEST_PARCEL_UUID, snapshot.getActiveDataSubscriptionGroup()); + } + + @Test public void testOnSubscriptionsChangedFired_WithReadySubidsNoPrivilegedPackages() throws Exception { setupReadySubIds(); @@ -371,7 +408,8 @@ public class TelephonySubscriptionTrackerTest { @Test public void testTelephonySubscriptionSnapshotGetGroupForSubId() throws Exception { final TelephonySubscriptionSnapshot snapshot = - new TelephonySubscriptionSnapshot(TEST_SUBID_TO_INFO_MAP, emptyMap()); + new TelephonySubscriptionSnapshot( + TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap()); assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_1)); assertEquals(TEST_PARCEL_UUID, snapshot.getGroupForSubId(TEST_SUBSCRIPTION_ID_2)); @@ -380,7 +418,8 @@ public class TelephonySubscriptionTrackerTest { @Test public void testTelephonySubscriptionSnapshotGetAllSubIdsInGroup() throws Exception { final TelephonySubscriptionSnapshot snapshot = - new TelephonySubscriptionSnapshot(TEST_SUBID_TO_INFO_MAP, emptyMap()); + new TelephonySubscriptionSnapshot( + TEST_SUBSCRIPTION_ID_1, TEST_SUBID_TO_INFO_MAP, emptyMap()); assertEquals( new ArraySet<>(Arrays.asList(TEST_SUBSCRIPTION_ID_1, TEST_SUBSCRIPTION_ID_2)), diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java index 6bfbfb1c8496..0f84f6ebe522 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java @@ -578,6 +578,10 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection mGatewayConnection.teardownAsynchronously(); mTestLooper.dispatchAll(); + // Verify that sending a non-quitting disconnect request does not unset the isQuitting flag + mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false); + mTestLooper.dispatchAll(); + assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); assertTrue(mGatewayConnection.isQuitting()); } diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java index acc8bf98e95b..d1f3a210d870 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java @@ -127,6 +127,10 @@ public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectio mGatewayConnection.teardownAsynchronously(); mTestLooper.dispatchAll(); + // Verify that sending a non-quitting disconnect request does not unset the isQuitting flag + mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false); + mTestLooper.dispatchAll(); + assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); assertTrue(mGatewayConnection.isQuitting()); } diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java index ac0edaa3b579..2056eea42ce6 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java @@ -68,7 +68,7 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect true /* isMobileDataEnabled */, mDeps); - vgc.setIsQuitting(true); + vgc.setQuitting(); vgc.transitionTo(vgc.mDisconnectedState); mTestLooper.dispatchAll(); @@ -102,6 +102,10 @@ public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnect mGatewayConnection.teardownAsynchronously(); mTestLooper.dispatchAll(); + // Verify that sending a non-quitting disconnect request does not unset the isQuitting flag + mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false); + mTestLooper.dispatchAll(); + assertNull(mGatewayConnection.getCurrentState()); verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any()); verifySafeModeTimeoutAlarmAndGetCallback(true /* expectCanceled */); diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java index 9da8b451c9fc..78aefad9f8ff 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java @@ -79,6 +79,10 @@ public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnec mGatewayConnection.teardownAsynchronously(); mTestLooper.dispatchAll(); + // Verify that sending a non-quitting disconnect request does not unset the isQuitting flag + mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false); + mTestLooper.dispatchAll(); + // Should do nothing; already tearing down. assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState()); verifyTeardownTimeoutAlarmAndGetCallback(false /* expectCanceled */); diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java index 69407657b3c4..1c859790a2fe 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java @@ -90,6 +90,10 @@ public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnect .onSelectedUnderlyingNetworkChanged(null); mTestLooper.dispatchAll(); + // Verify that sending a non-quitting disconnect request does not unset the isQuitting flag + mGatewayConnection.sendDisconnectRequestedAndAcquireWakelock("TEST", false); + mTestLooper.dispatchAll(); + assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState()); verifyRetryTimeoutAlarmAndGetCallback(mFirstRetryInterval, true /* expectCanceled */); diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java index b97023a95d72..a696b3ae28f7 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -127,7 +127,9 @@ public class VcnGatewayConnectionTestBase { protected static final TelephonySubscriptionSnapshot TEST_SUBSCRIPTION_SNAPSHOT = new TelephonySubscriptionSnapshot( - Collections.singletonMap(TEST_SUB_ID, TEST_SUB_INFO), Collections.EMPTY_MAP); + TEST_SUB_ID, + Collections.singletonMap(TEST_SUB_ID, TEST_SUB_INFO), + Collections.EMPTY_MAP); @NonNull protected final Context mContext; @NonNull protected final TestLooper mTestLooper; |