blob: 5a96bcbb04a1b0087d838af0195f523d8343be0f [file] [log] [blame]
/*
* Copyright (C) 2019 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.net.module.util;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY;
import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.net.module.util.FeatureVersions.CONNECTIVITY_MODULE_ID;
import static com.android.net.module.util.FeatureVersions.NETWORK_STACK_MODULE_ID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.provider.DeviceConfig;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.MockitoSession;
import java.util.Arrays;
/**
* Tests for DeviceConfigUtils.
*
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
public class DeviceConfigUtilsTest {
private static final String TEST_NAME_SPACE = "connectivity";
private static final String TEST_EXPERIMENT_FLAG = "experiment_flag";
private static final int TEST_FLAG_VALUE = 28;
private static final String TEST_FLAG_VALUE_STRING = "28";
private static final int TEST_DEFAULT_FLAG_VALUE = 0;
private static final int TEST_MAX_FLAG_VALUE = 1000;
private static final int TEST_MIN_FLAG_VALUE = 100;
private static final long TEST_PACKAGE_VERSION = 290000000;
private static final String TEST_PACKAGE_NAME = "test.package.name";
// The APEX name is the name of the APEX module, as in android.content.pm.ModuleInfo, and is
// used for its mount point in /apex. APEX packages are actually APKs with a different
// file extension, so they have an AndroidManifest: the APEX package name is the package name in
// that manifest, and is reflected in android.content.pm.ApplicationInfo. Contrary to the APEX
// (module) name, different package names are typically used to identify the organization that
// built and signed the APEX modules.
private static final String TEST_APEX_PACKAGE_NAME = "com.prefix.android.tethering";
private static final String TEST_GO_APEX_PACKAGE_NAME = "com.prefix.android.go.tethering";
private static final String TEST_CONNRES_PACKAGE_NAME =
"com.prefix.android.connectivity.resources";
private static final String TEST_NETWORKSTACK_NAME = "com.prefix.android.networkstack";
private static final String TEST_GO_NETWORKSTACK_NAME = "com.prefix.android.go.networkstack";
private final PackageInfo mPackageInfo = new PackageInfo();
private final PackageInfo mApexPackageInfo = new PackageInfo();
private MockitoSession mSession;
@Mock private Context mContext;
@Mock private PackageManager mPm;
@Mock private Resources mResources;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mSession = mockitoSession().spyStatic(DeviceConfig.class).startMocking();
mPackageInfo.setLongVersionCode(TEST_PACKAGE_VERSION);
mApexPackageInfo.setLongVersionCode(TEST_PACKAGE_VERSION);
doReturn(mPm).when(mContext).getPackageManager();
doReturn(TEST_PACKAGE_NAME).when(mContext).getPackageName();
doThrow(NameNotFoundException.class).when(mPm).getPackageInfo(anyString(), anyInt());
doReturn(mPackageInfo).when(mPm).getPackageInfo(eq(TEST_PACKAGE_NAME), anyInt());
doReturn(mApexPackageInfo).when(mPm).getPackageInfo(eq(TEST_APEX_PACKAGE_NAME), anyInt());
doReturn(mResources).when(mContext).getResources();
final ResolveInfo ri = new ResolveInfo();
ri.activityInfo = new ActivityInfo();
ri.activityInfo.applicationInfo = new ApplicationInfo();
ri.activityInfo.applicationInfo.packageName = TEST_CONNRES_PACKAGE_NAME;
ri.activityInfo.applicationInfo.sourceDir =
"/apex/com.android.tethering/priv-app/ServiceConnectivityResources@version";
doReturn(Arrays.asList(ri)).when(mPm).queryIntentActivities(argThat(
intent -> intent.getAction().equals(DeviceConfigUtils.RESOURCES_APK_INTENT)),
eq(MATCH_SYSTEM_ONLY));
}
@After
public void tearDown() {
mSession.finishMocking();
DeviceConfigUtils.resetPackageVersionCacheForTest();
}
@Test
public void testGetDeviceConfigPropertyInt_Null() {
doReturn(null).when(() -> DeviceConfig.getProperty(eq(TEST_NAME_SPACE),
eq(TEST_EXPERIMENT_FLAG)));
assertEquals(TEST_DEFAULT_FLAG_VALUE, DeviceConfigUtils.getDeviceConfigPropertyInt(
TEST_NAME_SPACE, TEST_EXPERIMENT_FLAG,
TEST_DEFAULT_FLAG_VALUE /* default value */));
}
@Test
public void testGetDeviceConfigPropertyInt_NotNull() {
doReturn(TEST_FLAG_VALUE_STRING).when(() -> DeviceConfig.getProperty(eq(TEST_NAME_SPACE),
eq(TEST_EXPERIMENT_FLAG)));
assertEquals(TEST_FLAG_VALUE, DeviceConfigUtils.getDeviceConfigPropertyInt(
TEST_NAME_SPACE, TEST_EXPERIMENT_FLAG,
TEST_DEFAULT_FLAG_VALUE /* default value */));
}
@Test
public void testGetDeviceConfigPropertyInt_NormalValue() {
doReturn(TEST_FLAG_VALUE_STRING).when(() -> DeviceConfig.getProperty(eq(TEST_NAME_SPACE),
eq(TEST_EXPERIMENT_FLAG)));
assertEquals(TEST_FLAG_VALUE, DeviceConfigUtils.getDeviceConfigPropertyInt(
TEST_NAME_SPACE, TEST_EXPERIMENT_FLAG, 0 /* minimum value */,
TEST_MAX_FLAG_VALUE /* maximum value */,
TEST_DEFAULT_FLAG_VALUE /* default value */));
}
@Test
public void testGetDeviceConfigPropertyInt_NullValue() {
doReturn(null).when(() -> DeviceConfig.getProperty(
eq(TEST_NAME_SPACE), eq(TEST_EXPERIMENT_FLAG)));
assertEquals(TEST_DEFAULT_FLAG_VALUE, DeviceConfigUtils.getDeviceConfigPropertyInt(
TEST_NAME_SPACE, TEST_EXPERIMENT_FLAG, 0 /* minimum value */,
TEST_MAX_FLAG_VALUE /* maximum value */,
TEST_DEFAULT_FLAG_VALUE /* default value */));
}
@Test
public void testGetDeviceConfigPropertyInt_OverMaximumValue() {
doReturn(Integer.toString(TEST_MAX_FLAG_VALUE + 10)).when(() -> DeviceConfig.getProperty(
eq(TEST_NAME_SPACE), eq(TEST_EXPERIMENT_FLAG)));
assertEquals(TEST_DEFAULT_FLAG_VALUE, DeviceConfigUtils.getDeviceConfigPropertyInt(
TEST_NAME_SPACE, TEST_EXPERIMENT_FLAG, TEST_MIN_FLAG_VALUE /* minimum value */,
TEST_MAX_FLAG_VALUE /* maximum value */,
TEST_DEFAULT_FLAG_VALUE /* default value */));
}
@Test
public void testGetDeviceConfigPropertyInt_EqualsMaximumValue() {
doReturn(Integer.toString(TEST_MAX_FLAG_VALUE)).when(() -> DeviceConfig.getProperty(
eq(TEST_NAME_SPACE), eq(TEST_EXPERIMENT_FLAG)));
assertEquals(TEST_MAX_FLAG_VALUE, DeviceConfigUtils.getDeviceConfigPropertyInt(
TEST_NAME_SPACE, TEST_EXPERIMENT_FLAG, TEST_MIN_FLAG_VALUE /* minimum value */,
TEST_MAX_FLAG_VALUE /* maximum value */,
TEST_DEFAULT_FLAG_VALUE /* default value */));
}
@Test
public void testGetDeviceConfigPropertyInt_BelowMinimumValue() {
doReturn(Integer.toString(TEST_MIN_FLAG_VALUE - 10)).when(() -> DeviceConfig.getProperty(
eq(TEST_NAME_SPACE), eq(TEST_EXPERIMENT_FLAG)));
assertEquals(TEST_DEFAULT_FLAG_VALUE, DeviceConfigUtils.getDeviceConfigPropertyInt(
TEST_NAME_SPACE, TEST_EXPERIMENT_FLAG, TEST_MIN_FLAG_VALUE /* minimum value */,
TEST_MAX_FLAG_VALUE /* maximum value */,
TEST_DEFAULT_FLAG_VALUE /* default value */));
}
@Test
public void testGetDeviceConfigPropertyInt_EqualsMinimumValue() {
doReturn(Integer.toString(TEST_MIN_FLAG_VALUE)).when(() -> DeviceConfig.getProperty(
eq(TEST_NAME_SPACE), eq(TEST_EXPERIMENT_FLAG)));
assertEquals(TEST_MIN_FLAG_VALUE, DeviceConfigUtils.getDeviceConfigPropertyInt(
TEST_NAME_SPACE, TEST_EXPERIMENT_FLAG, TEST_MIN_FLAG_VALUE /* minimum value */,
TEST_MAX_FLAG_VALUE /* maximum value */,
TEST_DEFAULT_FLAG_VALUE /* default value */));
}
@Test
public void testGetDeviceConfigPropertyBoolean_Null() {
doReturn(null).when(() -> DeviceConfig.getProperty(eq(TEST_NAME_SPACE),
eq(TEST_EXPERIMENT_FLAG)));
assertFalse(DeviceConfigUtils.getDeviceConfigPropertyBoolean(
TEST_NAME_SPACE, TEST_EXPERIMENT_FLAG,
false /* default value */));
}
@Test
public void testGetDeviceConfigPropertyBoolean_NotNull() {
doReturn("true").when(() -> DeviceConfig.getProperty(eq(TEST_NAME_SPACE),
eq(TEST_EXPERIMENT_FLAG)));
assertTrue(DeviceConfigUtils.getDeviceConfigPropertyBoolean(
TEST_NAME_SPACE, TEST_EXPERIMENT_FLAG,
false /* default value */));
}
@Test
public void testIsNetworkStackFeatureEnabled() {
doReturn(TEST_FLAG_VALUE_STRING).when(() -> DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY,
TEST_EXPERIMENT_FLAG));
assertTrue(DeviceConfigUtils.isNetworkStackFeatureEnabled(mContext, TEST_EXPERIMENT_FLAG));
}
@Test
public void testIsTetheringFeatureEnabled() {
doReturn(TEST_FLAG_VALUE_STRING).when(() -> DeviceConfig.getProperty(NAMESPACE_TETHERING,
TEST_EXPERIMENT_FLAG));
assertTrue(DeviceConfigUtils.isTetheringFeatureEnabled(mContext, TEST_EXPERIMENT_FLAG));
}
@Test
public void testFeatureDefaultEnabled() {
doReturn(null).when(() -> DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY,
TEST_EXPERIMENT_FLAG));
doReturn(null).when(() -> DeviceConfig.getProperty(NAMESPACE_TETHERING,
TEST_EXPERIMENT_FLAG));
assertFalse(DeviceConfigUtils.isNetworkStackFeatureEnabled(mContext, TEST_EXPERIMENT_FLAG));
assertFalse(DeviceConfigUtils.isTetheringFeatureEnabled(mContext, TEST_EXPERIMENT_FLAG));
}
@Test
public void testFeatureIsEnabledWithException() throws Exception {
doThrow(NameNotFoundException.class).when(mPm).getPackageInfo(anyString(), anyInt());
// Feature should be enabled by flag value "1".
doReturn("1").when(() -> DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY,
TEST_EXPERIMENT_FLAG));
doReturn("1").when(() -> DeviceConfig.getProperty(NAMESPACE_TETHERING,
TEST_EXPERIMENT_FLAG));
assertTrue(DeviceConfigUtils.isNetworkStackFeatureEnabled(mContext, TEST_EXPERIMENT_FLAG));
assertTrue(DeviceConfigUtils.isTetheringFeatureEnabled(mContext, TEST_EXPERIMENT_FLAG));
// Feature should be disabled by flag value "999999999".
doReturn("999999999").when(() -> DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY,
TEST_EXPERIMENT_FLAG));
doReturn("999999999").when(() -> DeviceConfig.getProperty(NAMESPACE_TETHERING,
TEST_EXPERIMENT_FLAG));
assertFalse(DeviceConfigUtils.isNetworkStackFeatureEnabled(mContext, TEST_EXPERIMENT_FLAG));
assertFalse(DeviceConfigUtils.isTetheringFeatureEnabled(mContext, TEST_EXPERIMENT_FLAG));
// Follow defaultEnabled if the flag is not set
doReturn(null).when(() -> DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY,
TEST_EXPERIMENT_FLAG));
doReturn(null).when(() -> DeviceConfig.getProperty(NAMESPACE_TETHERING,
TEST_EXPERIMENT_FLAG));
assertFalse(DeviceConfigUtils.isNetworkStackFeatureEnabled(mContext, TEST_EXPERIMENT_FLAG,
false /* defaultEnabled */));
assertTrue(DeviceConfigUtils.isNetworkStackFeatureEnabled(mContext, TEST_EXPERIMENT_FLAG,
true /* defaultEnabled */));
assertFalse(DeviceConfigUtils.isTetheringFeatureEnabled(mContext, TEST_EXPERIMENT_FLAG));
}
@Test
public void testFeatureIsEnabledOnGo() throws Exception {
doThrow(NameNotFoundException.class).when(mPm).getPackageInfo(
eq(TEST_APEX_PACKAGE_NAME), anyInt());
doReturn(mApexPackageInfo).when(mPm).getPackageInfo(
eq(TEST_GO_APEX_PACKAGE_NAME), anyInt());
doReturn("0").when(() -> DeviceConfig.getProperty(
NAMESPACE_CONNECTIVITY, TEST_EXPERIMENT_FLAG));
doReturn("0").when(() -> DeviceConfig.getProperty(
NAMESPACE_TETHERING, TEST_EXPERIMENT_FLAG));
assertFalse(DeviceConfigUtils.isNetworkStackFeatureEnabled(mContext, TEST_EXPERIMENT_FLAG));
assertFalse(DeviceConfigUtils.isTetheringFeatureEnabled(mContext, TEST_EXPERIMENT_FLAG));
doReturn(TEST_FLAG_VALUE_STRING).when(() -> DeviceConfig.getProperty(NAMESPACE_TETHERING,
TEST_EXPERIMENT_FLAG));
assertTrue(DeviceConfigUtils.isTetheringFeatureEnabled(mContext, TEST_EXPERIMENT_FLAG));
}
@Test
public void testIsNetworkStackFeatureEnabledCaching() throws Exception {
doReturn(TEST_FLAG_VALUE_STRING).when(() -> DeviceConfig.getProperty(NAMESPACE_CONNECTIVITY,
TEST_EXPERIMENT_FLAG));
assertTrue(DeviceConfigUtils.isNetworkStackFeatureEnabled(mContext, TEST_EXPERIMENT_FLAG));
assertTrue(DeviceConfigUtils.isNetworkStackFeatureEnabled(mContext, TEST_EXPERIMENT_FLAG));
// Package info is only queried once
verify(mContext, times(1)).getPackageManager();
verify(mContext, times(1)).getPackageName();
verify(mPm, times(1)).getPackageInfo(anyString(), anyInt());
}
@Test
public void testIsTetheringFeatureEnabledCaching() throws Exception {
doReturn(TEST_FLAG_VALUE_STRING).when(() -> DeviceConfig.getProperty(NAMESPACE_TETHERING,
TEST_EXPERIMENT_FLAG));
assertTrue(DeviceConfigUtils.isTetheringFeatureEnabled(mContext, TEST_EXPERIMENT_FLAG));
assertTrue(DeviceConfigUtils.isTetheringFeatureEnabled(mContext, TEST_EXPERIMENT_FLAG));
// Package info is only queried once
verify(mPm, times(1)).getPackageInfo(anyString(), anyInt());
verify(mContext, never()).getPackageName();
}
@Test
public void testGetResBooleanConfig() {
final int someResId = 1234;
doReturn(true).when(mResources).getBoolean(someResId);
assertTrue(DeviceConfigUtils.getResBooleanConfig(mContext, someResId, false));
doReturn(false).when(mResources).getBoolean(someResId);
assertFalse(DeviceConfigUtils.getResBooleanConfig(mContext, someResId, false));
doThrow(new Resources.NotFoundException()).when(mResources).getBoolean(someResId);
assertFalse(DeviceConfigUtils.getResBooleanConfig(mContext, someResId, false));
}
@Test
public void testGetResIntegerConfig() {
final int someResId = 1234;
doReturn(2097).when(mResources).getInteger(someResId);
assertEquals(2097, DeviceConfigUtils.getResIntegerConfig(mContext, someResId, 2098));
doThrow(new Resources.NotFoundException()).when(mResources).getInteger(someResId);
assertEquals(2098, DeviceConfigUtils.getResIntegerConfig(mContext, someResId, 2098));
}
@Test
public void testGetNetworkStackModuleVersionCaching() throws Exception {
final PackageInfo networkStackPackageInfo = new PackageInfo();
networkStackPackageInfo.setLongVersionCode(TEST_PACKAGE_VERSION);
doReturn(networkStackPackageInfo).when(mPm).getPackageInfo(
eq(TEST_NETWORKSTACK_NAME), anyInt());
assertEquals(TEST_PACKAGE_VERSION,
DeviceConfigUtils.getNetworkStackModuleVersion(mContext));
assertEquals(TEST_PACKAGE_VERSION,
DeviceConfigUtils.getNetworkStackModuleVersion(mContext));
// Package info is only queried once
verify(mPm, times(1)).getPackageInfo(anyString(), anyInt());
verify(mContext, never()).getPackageName();
}
@Test
public void testGetNetworkStackModuleVersionOnNonMainline() {
assertEquals(DeviceConfigUtils.DEFAULT_PACKAGE_VERSION,
DeviceConfigUtils.getNetworkStackModuleVersion(mContext));
}
@Test
public void testGetNetworkStackModuleVersion() throws Exception {
final PackageInfo networkStackPackageInfo = new PackageInfo();
final PackageInfo goNetworkStackPackageInfo = new PackageInfo();
networkStackPackageInfo.setLongVersionCode(TEST_PACKAGE_VERSION);
goNetworkStackPackageInfo.setLongVersionCode(TEST_PACKAGE_VERSION + 1);
doReturn(goNetworkStackPackageInfo).when(mPm).getPackageInfo(
eq(TEST_NETWORKSTACK_NAME), anyInt());
// Verify the returned value is go module version.
assertEquals(TEST_PACKAGE_VERSION + 1,
DeviceConfigUtils.getNetworkStackModuleVersion(mContext));
}
@Test
public void testIsFeatureSupported_networkStackFeature() throws Exception {
// Supported for DEFAULT_PACKAGE_VERSION
assertTrue(DeviceConfigUtils.isFeatureSupported(
mContext, TEST_PACKAGE_VERSION + NETWORK_STACK_MODULE_ID));
final PackageInfo networkStackPackageInfo = new PackageInfo();
networkStackPackageInfo.setLongVersionCode(TEST_PACKAGE_VERSION);
doReturn(networkStackPackageInfo).when(mPm).getPackageInfo(
eq(TEST_NETWORKSTACK_NAME), anyInt());
assertTrue(DeviceConfigUtils.isFeatureSupported(
mContext, TEST_PACKAGE_VERSION + NETWORK_STACK_MODULE_ID));
assertFalse(DeviceConfigUtils.isFeatureSupported(
mContext, TEST_PACKAGE_VERSION + NETWORK_STACK_MODULE_ID + 1));
}
@Test
public void testIsFeatureSupported_tetheringFeature() throws Exception {
assertTrue(DeviceConfigUtils.isFeatureSupported(
mContext, TEST_PACKAGE_VERSION + CONNECTIVITY_MODULE_ID));
// Return false because feature requires a future version.
assertFalse(DeviceConfigUtils.isFeatureSupported(
mContext, 889900000L + CONNECTIVITY_MODULE_ID));
}
@Test
public void testIsFeatureSupported_illegalModule() throws Exception {
assertThrows(IllegalArgumentException.class,
() -> DeviceConfigUtils.isFeatureSupported(mContext, TEST_PACKAGE_VERSION));
}
@Test
public void testIsTetheringFeatureNotChickenedOut() throws Exception {
doReturn("0").when(() -> DeviceConfig.getProperty(
eq(NAMESPACE_TETHERING), eq(TEST_EXPERIMENT_FLAG)));
assertTrue(DeviceConfigUtils.isTetheringFeatureNotChickenedOut(TEST_EXPERIMENT_FLAG));
doReturn(TEST_FLAG_VALUE_STRING).when(
() -> DeviceConfig.getProperty(eq(NAMESPACE_TETHERING), eq(TEST_EXPERIMENT_FLAG)));
assertFalse(DeviceConfigUtils.isTetheringFeatureNotChickenedOut(TEST_EXPERIMENT_FLAG));
}
@Test
public void testIsNetworkStackFeatureNotChickenedOut() throws Exception {
doReturn("0").when(() -> DeviceConfig.getProperty(
eq(NAMESPACE_CONNECTIVITY), eq(TEST_EXPERIMENT_FLAG)));
assertTrue(DeviceConfigUtils.isNetworkStackFeatureNotChickenedOut(TEST_EXPERIMENT_FLAG));
doReturn(TEST_FLAG_VALUE_STRING).when(
() -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY),
eq(TEST_EXPERIMENT_FLAG)));
assertFalse(DeviceConfigUtils.isNetworkStackFeatureNotChickenedOut(TEST_EXPERIMENT_FLAG));
}
}