| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| import static org.mockito.Mockito.any; |
| import static org.mockito.Mockito.doReturn; |
| import static org.mockito.Mockito.eq; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.verify; |
| |
| import android.content.Context; |
| import android.net.ConnectivityManager; |
| import android.net.vcn.VcnConfig; |
| import android.net.vcn.VcnConfigTest; |
| import android.os.ParcelUuid; |
| import android.os.PersistableBundle; |
| import android.os.Process; |
| import android.os.UserHandle; |
| import android.os.test.TestLooper; |
| import android.telephony.SubscriptionInfo; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.TelephonyManager; |
| |
| import androidx.test.filters.SmallTest; |
| import androidx.test.runner.AndroidJUnit4; |
| |
| import com.android.server.vcn.util.PersistableBundleUtils; |
| |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.io.FileNotFoundException; |
| import java.util.Collections; |
| import java.util.Map; |
| import java.util.UUID; |
| |
| /** Tests for {@link VcnManagementService}. */ |
| @RunWith(AndroidJUnit4.class) |
| @SmallTest |
| public class VcnManagementServiceTest { |
| private static final ParcelUuid TEST_UUID_1 = new ParcelUuid(new UUID(0, 0)); |
| private static final ParcelUuid TEST_UUID_2 = new ParcelUuid(new UUID(1, 1)); |
| private static final VcnConfig TEST_VCN_CONFIG = VcnConfigTest.buildTestConfig(); |
| private static final Map<ParcelUuid, VcnConfig> TEST_VCN_CONFIG_MAP = |
| Collections.unmodifiableMap(Collections.singletonMap(TEST_UUID_1, TEST_VCN_CONFIG)); |
| |
| private static final SubscriptionInfo TEST_SUBSCRIPTION_INFO = |
| new SubscriptionInfo( |
| 1 /* id */, |
| "" /* iccId */, |
| 0 /* simSlotIndex */, |
| "Carrier" /* displayName */, |
| "Carrier" /* carrierName */, |
| 0 /* nameSource */, |
| 255 /* iconTint */, |
| "12345" /* number */, |
| 0 /* roaming */, |
| null /* icon */, |
| "0" /* mcc */, |
| "0" /* mnc */, |
| "0" /* countryIso */, |
| false /* isEmbedded */, |
| null /* nativeAccessRules */, |
| null /* cardString */, |
| false /* isOpportunistic */, |
| TEST_UUID_1.toString() /* groupUUID */, |
| 0 /* carrierId */, |
| 0 /* profileClass */); |
| |
| private final Context mMockContext = mock(Context.class); |
| private final VcnManagementService.Dependencies mMockDeps = |
| mock(VcnManagementService.Dependencies.class); |
| private final TestLooper mTestLooper = new TestLooper(); |
| private final ConnectivityManager mConnMgr = mock(ConnectivityManager.class); |
| private final TelephonyManager mTelMgr = mock(TelephonyManager.class); |
| private final SubscriptionManager mSubMgr = mock(SubscriptionManager.class); |
| private final VcnManagementService mVcnMgmtSvc; |
| private final PersistableBundleUtils.LockingReadWriteHelper mConfigReadWriteHelper = |
| mock(PersistableBundleUtils.LockingReadWriteHelper.class); |
| |
| public VcnManagementServiceTest() throws Exception { |
| setupSystemService(mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class); |
| setupSystemService(mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class); |
| setupSystemService( |
| mSubMgr, Context.TELEPHONY_SUBSCRIPTION_SERVICE, SubscriptionManager.class); |
| |
| doReturn(mTestLooper.getLooper()).when(mMockDeps).getLooper(); |
| doReturn(Process.FIRST_APPLICATION_UID).when(mMockDeps).getBinderCallingUid(); |
| doReturn(mConfigReadWriteHelper) |
| .when(mMockDeps) |
| .newPersistableBundleLockingReadWriteHelper(any()); |
| |
| final PersistableBundle bundle = |
| PersistableBundleUtils.fromMap( |
| TEST_VCN_CONFIG_MAP, |
| PersistableBundleUtils::fromParcelUuid, |
| VcnConfig::toPersistableBundle); |
| doReturn(bundle).when(mConfigReadWriteHelper).readFromDisk(); |
| |
| setupMockedCarrierPrivilege(true); |
| mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps); |
| } |
| |
| private void setupSystemService(Object service, String name, Class<?> serviceClass) { |
| doReturn(name).when(mMockContext).getSystemServiceName(serviceClass); |
| doReturn(service).when(mMockContext).getSystemService(name); |
| } |
| |
| private void setupMockedCarrierPrivilege(boolean isPrivileged) { |
| doReturn(Collections.singletonList(TEST_SUBSCRIPTION_INFO)) |
| .when(mSubMgr) |
| .getSubscriptionsInGroup(any()); |
| doReturn(isPrivileged) |
| .when(mTelMgr) |
| .hasCarrierPrivileges(eq(TEST_SUBSCRIPTION_INFO.getSubscriptionId())); |
| } |
| |
| @Test |
| public void testSystemReady() throws Exception { |
| mVcnMgmtSvc.systemReady(); |
| |
| verify(mConnMgr) |
| .registerNetworkProvider(any(VcnManagementService.VcnNetworkProvider.class)); |
| } |
| |
| @Test |
| public void testNonSystemServerRealConfigFileAccessPermission() throws Exception { |
| // Attempt to build a real instance of the dependencies, and verify we cannot write to the |
| // file. |
| VcnManagementService.Dependencies deps = new VcnManagementService.Dependencies(); |
| PersistableBundleUtils.LockingReadWriteHelper configReadWriteHelper = |
| deps.newPersistableBundleLockingReadWriteHelper( |
| VcnManagementService.VCN_CONFIG_FILE); |
| |
| // Even tests should not be able to read/write configs from disk; SELinux policies restrict |
| // it to only the system server. |
| // Reading config should always return null since the file "does not exist", and writing |
| // should throw an IOException. |
| assertNull(configReadWriteHelper.readFromDisk()); |
| |
| try { |
| configReadWriteHelper.writeToDisk(new PersistableBundle()); |
| fail("Expected IOException due to SELinux policy"); |
| } catch (FileNotFoundException expected) { |
| } |
| } |
| |
| @Test |
| public void testLoadVcnConfigsOnStartup() throws Exception { |
| mTestLooper.dispatchAll(); |
| |
| assertEquals(TEST_VCN_CONFIG_MAP, mVcnMgmtSvc.getConfigs()); |
| verify(mConfigReadWriteHelper).readFromDisk(); |
| } |
| |
| @Test |
| public void testSetVcnConfigRequiresNonSystemServer() throws Exception { |
| doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid(); |
| |
| try { |
| mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig()); |
| fail("Expected IllegalStateException exception for system server"); |
| } catch (IllegalStateException expected) { |
| } |
| } |
| |
| @Test |
| public void testSetVcnConfigRequiresSystemUser() throws Exception { |
| doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, Process.FIRST_APPLICATION_UID)) |
| .when(mMockDeps) |
| .getBinderCallingUid(); |
| |
| try { |
| mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig()); |
| fail("Expected security exception for non system user"); |
| } catch (SecurityException expected) { |
| } |
| } |
| |
| @Test |
| public void testSetVcnConfigRequiresCarrierPrivileges() throws Exception { |
| setupMockedCarrierPrivilege(false); |
| |
| try { |
| mVcnMgmtSvc.setVcnConfig(TEST_UUID_1, VcnConfigTest.buildTestConfig()); |
| fail("Expected security exception for missing carrier privileges"); |
| } catch (SecurityException expected) { |
| } |
| } |
| |
| @Test |
| public void testSetVcnConfig() throws Exception { |
| // Use a different UUID to simulate a new VCN config. |
| mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG); |
| assertEquals(TEST_VCN_CONFIG, mVcnMgmtSvc.getConfigs().get(TEST_UUID_2)); |
| verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class)); |
| } |
| |
| @Test |
| public void testClearVcnConfigRequiresNonSystemServer() throws Exception { |
| doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid(); |
| |
| try { |
| mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1); |
| fail("Expected IllegalStateException exception for system server"); |
| } catch (IllegalStateException expected) { |
| } |
| } |
| |
| @Test |
| public void testClearVcnConfigRequiresSystemUser() throws Exception { |
| doReturn(UserHandle.getUid(UserHandle.MIN_SECONDARY_USER_ID, Process.FIRST_APPLICATION_UID)) |
| .when(mMockDeps) |
| .getBinderCallingUid(); |
| |
| try { |
| mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1); |
| fail("Expected security exception for non system user"); |
| } catch (SecurityException expected) { |
| } |
| } |
| |
| @Test |
| public void testClearVcnConfigRequiresCarrierPrivileges() throws Exception { |
| setupMockedCarrierPrivilege(false); |
| |
| try { |
| mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1); |
| fail("Expected security exception for missing carrier privileges"); |
| } catch (SecurityException expected) { |
| } |
| } |
| |
| @Test |
| public void testClearVcnConfig() throws Exception { |
| mVcnMgmtSvc.clearVcnConfig(TEST_UUID_1); |
| assertTrue(mVcnMgmtSvc.getConfigs().isEmpty()); |
| verify(mConfigReadWriteHelper).writeToDisk(any(PersistableBundle.class)); |
| } |
| } |