1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net.cts;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.assertNotEquals;
22 import static org.junit.Assert.assertNotNull;
23 import static org.junit.Assume.assumeTrue;
24 
25 import android.content.Context;
26 import android.net.ConnectivityManager;
27 import android.platform.test.annotations.AppModeFull;
28 import android.os.FileUtils;
29 import android.os.ParcelFileDescriptor;
30 
31 import androidx.test.InstrumentationRegistry;
32 import androidx.test.filters.SmallTest;
33 import androidx.test.runner.AndroidJUnit4;
34 
35 import com.android.compatibility.common.util.ApiLevelUtil;
36 import com.android.compatibility.common.util.SystemUtil;
37 
38 import org.junit.After;
39 import org.junit.Assert;
40 import org.junit.Before;
41 import org.junit.Test;
42 import org.junit.runner.RunWith;
43 
44 import java.io.FileOutputStream;
45 import java.io.IOException;
46 import java.io.InputStream;
47 import java.util.Formatter;
48 
49 @SmallTest
50 @RunWith(AndroidJUnit4.class)
51 public class NetworkWatchlistTest {
52 
53     private static final String TEST_WATCHLIST_XML = "assets/network_watchlist_config_for_test.xml";
54     private static final String TEST_EMPTY_WATCHLIST_XML =
55             "assets/network_watchlist_config_empty_for_test.xml";
56     private static final String TMP_CONFIG_PATH =
57             "/data/local/tmp/network_watchlist_config_for_test.xml";
58     // Generated from sha256sum network_watchlist_config_for_test.xml
59     private static final String TEST_WATCHLIST_CONFIG_HASH =
60             "B5FC4636994180D54E1E912F78178AB1D8BD2BE71D90CA9F5BBC3284E4D04ED4";
61 
62     private ConnectivityManager mConnectivityManager;
63     private boolean mHasFeature;
64 
65     @Before
setUp()66     public void setUp() throws Exception {
67         mHasFeature = isAtLeastP();
68         mConnectivityManager =
69                 (ConnectivityManager) InstrumentationRegistry.getContext().getSystemService(
70                         Context.CONNECTIVITY_SERVICE);
71         assumeTrue(mHasFeature);
72         // Set empty watchlist test config before testing
73         setWatchlistConfig(TEST_EMPTY_WATCHLIST_XML);
74         // Verify test watchlist config is not set before testing
75         byte[] result = mConnectivityManager.getNetworkWatchlistConfigHash();
76         assertNotNull("Watchlist config does not exist", result);
77         assertNotEquals(TEST_WATCHLIST_CONFIG_HASH, byteArrayToHexString(result));
78     }
79 
80     @After
tearDown()81     public void tearDown() throws Exception {
82         if (mHasFeature) {
83             // Set empty watchlist test config after testing
84             setWatchlistConfig(TEST_EMPTY_WATCHLIST_XML);
85         }
86     }
87 
cleanup()88     private void cleanup() throws IOException {
89         runCommand("rm " + TMP_CONFIG_PATH);
90     }
91 
isAtLeastP()92     private boolean isAtLeastP() throws Exception {
93         // TODO: replace with ApiLevelUtil.isAtLeast(Build.VERSION_CODES.P) when the P API level
94         // constant is defined.
95         return ApiLevelUtil.getCodename().compareToIgnoreCase("P") >= 0;
96     }
97 
98     /**
99      * Test if ConnectivityManager.getNetworkWatchlistConfigHash() correctly
100      * returns the hash of config we set.
101      */
102     @Test
103     @AppModeFull(reason = "Cannot access resource file in instant app mode")
testGetWatchlistConfigHash()104     public void testGetWatchlistConfigHash() throws Exception {
105         // Set watchlist config file for test
106         setWatchlistConfig(TEST_WATCHLIST_XML);
107         // Test if watchlist config hash value is correct
108         byte[] result = mConnectivityManager.getNetworkWatchlistConfigHash();
109         Assert.assertEquals(TEST_WATCHLIST_CONFIG_HASH, byteArrayToHexString(result));
110     }
111 
byteArrayToHexString(byte[] bytes)112     private static String byteArrayToHexString(byte[] bytes) {
113         Formatter formatter = new Formatter();
114         for (byte b : bytes) {
115             formatter.format("%02X", b);
116         }
117         return formatter.toString();
118     }
119 
saveResourceToFile(String res, String filePath)120     private void saveResourceToFile(String res, String filePath) throws IOException {
121         // App can't access /data/local/tmp directly, so we pipe resource to file through stdin.
122         ParcelFileDescriptor stdin = pipeFromStdin(filePath);
123         pipeResourceToFileDescriptor(res, stdin);
124     }
125 
126     /* Pipe stdin to a file in filePath. Returns PFD for stdin. */
pipeFromStdin(String filePath)127     private ParcelFileDescriptor pipeFromStdin(String filePath) {
128         // Not all devices have symlink for /dev/stdin, so use /proc/self/fd/0 directly.
129         // /dev/stdin maps to /proc/self/fd/0.
130         return runRwCommand("cp /proc/self/fd/0 " + filePath)[1];
131     }
132 
pipeResourceToFileDescriptor(String res, ParcelFileDescriptor pfd)133     private void pipeResourceToFileDescriptor(String res, ParcelFileDescriptor pfd)
134             throws IOException {
135         InputStream resStream = getClass().getClassLoader().getResourceAsStream(res);
136         FileOutputStream fdStream = new ParcelFileDescriptor.AutoCloseOutputStream(pfd);
137 
138         FileUtils.copy(resStream, fdStream);
139 
140         try {
141             fdStream.close();
142         } catch (IOException e) {
143         }
144     }
145 
runCommand(String command)146     private static String runCommand(String command) throws IOException {
147         return SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(), command);
148     }
149 
runRwCommand(String command)150     private static ParcelFileDescriptor[] runRwCommand(String command) {
151         return InstrumentationRegistry.getInstrumentation()
152                 .getUiAutomation().executeShellCommandRw(command);
153     }
154 
setWatchlistConfig(String watchlistConfigFile)155     private void setWatchlistConfig(String watchlistConfigFile) throws Exception {
156         cleanup();
157         saveResourceToFile(watchlistConfigFile, TMP_CONFIG_PATH);
158         final String cmdResult = runCommand(
159                 "cmd network_watchlist set-test-config " + TMP_CONFIG_PATH).trim();
160         assertThat(cmdResult).contains("Success");
161         cleanup();
162     }
163 }
164