diff options
-rw-r--r-- | src/android/net/util/NetworkStackUtils.java | 29 | ||||
-rw-r--r-- | tests/unit/src/android/net/util/NetworkStackUtilsTest.java | 146 |
2 files changed, 175 insertions, 0 deletions
diff --git a/src/android/net/util/NetworkStackUtils.java b/src/android/net/util/NetworkStackUtils.java index 596e5e3..0a18c0e 100644 --- a/src/android/net/util/NetworkStackUtils.java +++ b/src/android/net/util/NetworkStackUtils.java @@ -18,7 +18,10 @@ package android.net.util; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Context; +import android.content.pm.PackageManager.NameNotFoundException; import android.provider.DeviceConfig; +import android.util.Log; import android.util.SparseArray; import java.io.FileDescriptor; @@ -34,6 +37,8 @@ import java.util.function.Predicate; * Collection of utilities for the network stack. */ public class NetworkStackUtils { + private static final String TAG = "NetworkStackUtils"; + /** * A list of captive portal detection specifications used in addition to the fallback URLs. * Each spec has the format url@@/@@statusCodeRegex@@/@@contentRegex. Specs are separated @@ -216,6 +221,30 @@ public class NetworkStackUtils { } /** + * Check whether or not one specific experimental feature for a particular namespace from + * {@link DeviceConfig} is enabled by comparing NetworkStack module version {@link NetworkStack} + * with current version of property. If this property version is valid, the corresponding + * experimental feature would be enabled, otherwise disabled. + * @param context The global context information about an app environment. + * @param namespace The namespace containing the property to look up. + * @param name The name of the property to look up. + * @return true if this feature is enabled, or false if disabled. + */ + public static boolean isFeatureEnabled(@NonNull Context context, @NonNull String namespace, + @NonNull String name) { + try { + final int propertyVersion = getDeviceConfigPropertyInt(namespace, name, + 0 /* default value */); + final long packageVersion = context.getPackageManager().getPackageInfo( + context.getPackageName(), 0).getLongVersionCode(); + return (propertyVersion != 0 && packageVersion >= (long) propertyVersion); + } catch (NameNotFoundException e) { + Log.e(TAG, "Could not find the package name", e); + return false; + } + } + + /** * Attaches a socket filter that accepts DHCP packets to the given socket. */ public static native void attachDhcpFilter(FileDescriptor fd) throws SocketException; diff --git a/tests/unit/src/android/net/util/NetworkStackUtilsTest.java b/tests/unit/src/android/net/util/NetworkStackUtilsTest.java new file mode 100644 index 0000000..806054d --- /dev/null +++ b/tests/unit/src/android/net/util/NetworkStackUtilsTest.java @@ -0,0 +1,146 @@ +/* + * 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 android.net.util; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +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 android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +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; + + +/** + * Tests for NetworkStackUtils. + * + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NetworkStackUtilsTest { + private static final String TEST_NAME_SPACE = "connectivity"; + private static final String TEST_EXPERIMENT_FLAG = "experiment_flag"; + private static final int TEST_FLAG_VERSION = 28; + private static final String TEST_FLAG_VERSION_STRING = "28"; + private static final int TEST_DEFAULT_FLAG_VERSION = 0; + private static final long TEST_PACKAGE_VERSION = 290000000; + private static final String TEST_PACKAGE_NAME = "NetworkStackUtilsTest"; + private MockitoSession mSession; + + @Mock private Context mContext; + @Mock private PackageManager mPm; + @Mock private PackageInfo mPi; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mSession = mockitoSession().spyStatic(DeviceConfig.class).startMocking(); + + final PackageInfo pi = new PackageInfo(); + pi.setLongVersionCode(TEST_PACKAGE_VERSION); + + doReturn(mPm).when(mContext).getPackageManager(); + doReturn(TEST_PACKAGE_NAME).when(mContext).getPackageName(); + doReturn(pi).when(mPm).getPackageInfo(anyString(), anyInt()); + } + + @After + public void tearDown() { + mSession.finishMocking(); + } + + @Test + public void testGetDeviceConfigPropertyInt_Null() { + doReturn(null).when(() -> DeviceConfig.getProperty(eq(TEST_NAME_SPACE), + eq(TEST_EXPERIMENT_FLAG))); + assertEquals(TEST_DEFAULT_FLAG_VERSION, NetworkStackUtils.getDeviceConfigPropertyInt( + TEST_NAME_SPACE, TEST_EXPERIMENT_FLAG, + TEST_DEFAULT_FLAG_VERSION /* default value */)); + } + + @Test + public void testGetDeviceConfigPropertyInt_NotNull() { + doReturn(TEST_FLAG_VERSION_STRING).when(() -> DeviceConfig.getProperty(eq(TEST_NAME_SPACE), + eq(TEST_EXPERIMENT_FLAG))); + assertEquals(TEST_FLAG_VERSION, NetworkStackUtils.getDeviceConfigPropertyInt( + TEST_NAME_SPACE, TEST_EXPERIMENT_FLAG, + TEST_DEFAULT_FLAG_VERSION /* default value */)); + } + + @Test + public void testGetDeviceConfigPropertyBoolean_Null() { + doReturn(null).when(() -> DeviceConfig.getProperty(eq(TEST_NAME_SPACE), + eq(TEST_EXPERIMENT_FLAG))); + assertFalse(NetworkStackUtils.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(NetworkStackUtils.getDeviceConfigPropertyBoolean( + TEST_NAME_SPACE, TEST_EXPERIMENT_FLAG, + false /* default value */)); + } + + @Test + public void testFeatureIsEnabledWithExceptionsEnabled() { + doReturn(TEST_FLAG_VERSION_STRING).when(() -> DeviceConfig.getProperty(eq(TEST_NAME_SPACE), + eq(TEST_EXPERIMENT_FLAG))); + assertTrue(NetworkStackUtils.isFeatureEnabled(mContext, TEST_NAME_SPACE, + TEST_EXPERIMENT_FLAG)); + } + + @Test + public void testFeatureIsNotEnabled() { + doReturn(null).when(() -> DeviceConfig.getProperty(eq(TEST_NAME_SPACE), + eq(TEST_EXPERIMENT_FLAG))); + assertFalse(NetworkStackUtils.isFeatureEnabled(mContext, TEST_NAME_SPACE, + TEST_EXPERIMENT_FLAG)); + } + + @Test + public void testFeatureIsEnabledWithException() throws Exception { + doReturn(TEST_FLAG_VERSION_STRING).when(() -> DeviceConfig.getProperty(eq(TEST_NAME_SPACE), + eq(TEST_EXPERIMENT_FLAG))); + doThrow(NameNotFoundException.class).when(mPm).getPackageInfo(anyString(), anyInt()); + assertFalse(NetworkStackUtils.isFeatureEnabled(mContext, TEST_NAME_SPACE, + TEST_EXPERIMENT_FLAG)); + } +} |