diff options
author | Xiao Ma <xiaom@google.com> | 2019-09-10 15:47:33 +0900 |
---|---|---|
committer | Xiao Ma <xiaom@google.com> | 2019-09-26 17:49:54 +0900 |
commit | 08d1f4e878a08672ffa60245c4c4aa5999b4d608 (patch) | |
tree | 0716c080473a72e26cb6580b0c401bc5388c5350 | |
parent | dc9ef64019b294472c95a4130655141dc5e46fb6 (diff) |
Add a general method to check version validity of DeviceConfig property.
Considering boolean experimental flag is likely to cause misconfiguration,
particularly when NetworkStack module rolls back to previous version. It's
much safer to determine whether or not to enable one specific experimental
feature by comparing flag version with module version.
Test: atest NetworkStackTests
Change-Id: Ie0c9527eb87b75c998584f2b4439cc0e309e8b28
-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)); + } +} |