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;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNull;
22 import static org.junit.Assert.assertTrue;
23 import static org.mockito.Mockito.any;
24 import static org.mockito.Mockito.doAnswer;
25 import static org.mockito.Mockito.doNothing;
26 import static org.mockito.Mockito.doThrow;
27 import static org.mockito.Mockito.eq;
28 import static org.mockito.Mockito.inOrder;
29 import static org.mockito.Mockito.times;
30 import static org.mockito.Mockito.verify;
31 
32 import android.content.Context;
33 import android.net.ipmemorystore.Blob;
34 import android.net.ipmemorystore.IOnStatusListener;
35 import android.net.ipmemorystore.NetworkAttributes;
36 import android.net.ipmemorystore.NetworkAttributesParcelable;
37 import android.net.ipmemorystore.Status;
38 import android.net.networkstack.ModuleNetworkStackClient;
39 import android.os.Build;
40 import android.os.RemoteException;
41 
42 import androidx.test.filters.SmallTest;
43 
44 import com.android.testutils.DevSdkIgnoreRule;
45 import com.android.testutils.DevSdkIgnoreRunner;
46 
47 import org.junit.Before;
48 import org.junit.Test;
49 import org.junit.runner.RunWith;
50 import org.mockito.ArgumentCaptor;
51 import org.mockito.Captor;
52 import org.mockito.InOrder;
53 import org.mockito.Mock;
54 import org.mockito.MockitoAnnotations;
55 
56 import java.net.UnknownHostException;
57 import java.util.Arrays;
58 
59 @RunWith(DevSdkIgnoreRunner.class)
60 @SmallTest
61 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
62 public class IpMemoryStoreTest {
63     private static final String TAG = IpMemoryStoreTest.class.getSimpleName();
64     private static final String TEST_CLIENT_ID = "testClientId";
65     private static final String TEST_DATA_NAME = "testData";
66     private static final String TEST_OTHER_DATA_NAME = TEST_DATA_NAME + "Other";
67     private static final byte[] TEST_BLOB_DATA = new byte[] { -3, 6, 8, -9, 12,
68             -128, 0, 89, 112, 91, -34 };
69     private static final NetworkAttributes TEST_NETWORK_ATTRIBUTES = buildTestNetworkAttributes(
70             "hint", 219);
71 
72     @Mock
73     Context mMockContext;
74     @Mock
75     ModuleNetworkStackClient mModuleNetworkStackClient;
76     @Mock
77     IIpMemoryStore mMockService;
78     @Mock
79     IOnStatusListener mIOnStatusListener;
80     IpMemoryStore mStore;
81 
82     @Captor
83     ArgumentCaptor<IIpMemoryStoreCallbacks> mCbCaptor;
84     @Captor
85     ArgumentCaptor<NetworkAttributesParcelable> mNapCaptor;
86 
87     @Before
setUp()88     public void setUp() {
89         MockitoAnnotations.initMocks(this);
90     }
91 
startIpMemoryStore(boolean supplyService)92     private void startIpMemoryStore(boolean supplyService) {
93         if (supplyService) {
94             doAnswer(invocation -> {
95                 ((IIpMemoryStoreCallbacks) invocation.getArgument(0))
96                         .onIpMemoryStoreFetched(mMockService);
97                 return null;
98             }).when(mModuleNetworkStackClient).fetchIpMemoryStore(any());
99         } else {
100             doNothing().when(mModuleNetworkStackClient).fetchIpMemoryStore(mCbCaptor.capture());
101         }
102         mStore = new IpMemoryStore(mMockContext) {
103             @Override
104             protected ModuleNetworkStackClient getModuleNetworkStackClient(Context ctx) {
105                 return mModuleNetworkStackClient;
106             }
107         };
108     }
109 
buildTestNetworkAttributes(String hint, int mtu)110     private static NetworkAttributes buildTestNetworkAttributes(String hint, int mtu) {
111         return new NetworkAttributes.Builder()
112                 .setCluster(hint)
113                 .setMtu(mtu)
114                 .build();
115     }
116 
117     @Test
testNetworkAttributes()118     public void testNetworkAttributes() throws Exception {
119         startIpMemoryStore(true /* supplyService */);
120         final String l2Key = "fakeKey";
121 
122         mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES,
123                 status -> assertTrue("Store not successful : " + status.resultCode,
124                         status.isSuccess()));
125         verify(mMockService, times(1)).storeNetworkAttributes(eq(l2Key),
126                 mNapCaptor.capture(), any());
127         assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue()));
128 
129         mStore.retrieveNetworkAttributes(l2Key,
130                 (status, key, attr) -> {
131                     assertTrue("Retrieve network attributes not successful : "
132                             + status.resultCode, status.isSuccess());
133                     assertEquals(l2Key, key);
134                     assertEquals(TEST_NETWORK_ATTRIBUTES, attr);
135                 });
136 
137         verify(mMockService, times(1)).retrieveNetworkAttributes(eq(l2Key), any());
138     }
139 
140     @Test
testPrivateData()141     public void testPrivateData() throws RemoteException {
142         startIpMemoryStore(true /* supplyService */);
143         final Blob b = new Blob();
144         b.data = TEST_BLOB_DATA;
145         final String l2Key = "fakeKey";
146 
147         mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b,
148                 status -> {
149                     assertTrue("Store not successful : " + status.resultCode, status.isSuccess());
150                 });
151         verify(mMockService, times(1)).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME),
152                 eq(b), any());
153 
154         mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME,
155                 (status, key, name, data) -> {
156                     assertTrue("Retrieve blob status not successful : " + status.resultCode,
157                             status.isSuccess());
158                     assertEquals(l2Key, key);
159                     assertEquals(name, TEST_DATA_NAME);
160                     assertTrue(Arrays.equals(b.data, data.data));
161                 });
162         verify(mMockService, times(1)).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID),
163                 eq(TEST_OTHER_DATA_NAME), any());
164     }
165 
166     @Test
testFindL2Key()167     public void testFindL2Key()
168             throws UnknownHostException, RemoteException, Exception {
169         startIpMemoryStore(true /* supplyService */);
170         final String l2Key = "fakeKey";
171 
172         mStore.findL2Key(TEST_NETWORK_ATTRIBUTES,
173                 (status, key) -> {
174                     assertTrue("Retrieve network sameness not successful : " + status.resultCode,
175                             status.isSuccess());
176                     assertEquals(l2Key, key);
177                 });
178         verify(mMockService, times(1)).findL2Key(mNapCaptor.capture(), any());
179         assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue()));
180     }
181 
182     @Test
testIsSameNetwork()183     public void testIsSameNetwork() throws UnknownHostException, RemoteException {
184         startIpMemoryStore(true /* supplyService */);
185         final String l2Key1 = "fakeKey1";
186         final String l2Key2 = "fakeKey2";
187 
188         mStore.isSameNetwork(l2Key1, l2Key2,
189                 (status, answer) -> {
190                     assertFalse("Retrieve network sameness suspiciously successful : "
191                             + status.resultCode, status.isSuccess());
192                     assertEquals(Status.ERROR_ILLEGAL_ARGUMENT, status.resultCode);
193                     assertNull(answer);
194                 });
195         verify(mMockService, times(1)).isSameNetwork(eq(l2Key1), eq(l2Key2), any());
196     }
197 
198     @Test
testEnqueuedIpMsRequests()199     public void testEnqueuedIpMsRequests() throws Exception {
200         startIpMemoryStore(false /* supplyService */);
201 
202         final Blob b = new Blob();
203         b.data = TEST_BLOB_DATA;
204         final String l2Key = "fakeKey";
205 
206         // enqueue multiple ipms requests
207         mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES,
208                 status -> assertTrue("Store not successful : " + status.resultCode,
209                         status.isSuccess()));
210         mStore.retrieveNetworkAttributes(l2Key,
211                 (status, key, attr) -> {
212                     assertTrue("Retrieve network attributes not successful : "
213                             + status.resultCode, status.isSuccess());
214                     assertEquals(l2Key, key);
215                     assertEquals(TEST_NETWORK_ATTRIBUTES, attr);
216                 });
217         mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b,
218                 status -> assertTrue("Store not successful : " + status.resultCode,
219                         status.isSuccess()));
220         mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME,
221                 (status, key, name, data) -> {
222                     assertTrue("Retrieve blob status not successful : " + status.resultCode,
223                             status.isSuccess());
224                     assertEquals(l2Key, key);
225                     assertEquals(name, TEST_DATA_NAME);
226                     assertTrue(Arrays.equals(b.data, data.data));
227                 });
228 
229         // get ipms service ready
230         mCbCaptor.getValue().onIpMemoryStoreFetched(mMockService);
231 
232         InOrder inOrder = inOrder(mMockService);
233 
234         inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), any());
235         inOrder.verify(mMockService).retrieveNetworkAttributes(eq(l2Key), any());
236         inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME),
237                 eq(b), any());
238         inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID),
239                 eq(TEST_OTHER_DATA_NAME), any());
240         assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue()));
241     }
242 
243     @Test
testEnqueuedIpMsRequestsWithException()244     public void testEnqueuedIpMsRequestsWithException() throws Exception {
245         startIpMemoryStore(true /* supplyService */);
246         doThrow(RemoteException.class).when(mMockService).retrieveNetworkAttributes(any(), any());
247 
248         final Blob b = new Blob();
249         b.data = TEST_BLOB_DATA;
250         final String l2Key = "fakeKey";
251 
252         // enqueue multiple ipms requests
253         mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES,
254                 status -> assertTrue("Store not successful : " + status.resultCode,
255                         status.isSuccess()));
256         mStore.retrieveNetworkAttributes(l2Key,
257                 (status, key, attr) -> {
258                     assertTrue("Retrieve network attributes not successful : "
259                             + status.resultCode, status.isSuccess());
260                     assertEquals(l2Key, key);
261                     assertEquals(TEST_NETWORK_ATTRIBUTES, attr);
262                 });
263         mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b,
264                 status -> assertTrue("Store not successful : " + status.resultCode,
265                         status.isSuccess()));
266         mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME,
267                 (status, key, name, data) -> {
268                     assertTrue("Retrieve blob status not successful : " + status.resultCode,
269                             status.isSuccess());
270                     assertEquals(l2Key, key);
271                     assertEquals(name, TEST_DATA_NAME);
272                     assertTrue(Arrays.equals(b.data, data.data));
273                 });
274 
275         // verify the rest of the queue is still processed in order even if the remote exception
276         // occurs when calling one or more requests
277         InOrder inOrder = inOrder(mMockService);
278 
279         inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(), any());
280         inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME),
281                 eq(b), any());
282         inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID),
283                 eq(TEST_OTHER_DATA_NAME), any());
284         assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue()));
285     }
286 
287     @Test
testEnqueuedIpMsRequestsCallbackFunctionWithException()288     public void testEnqueuedIpMsRequestsCallbackFunctionWithException() throws Exception {
289         startIpMemoryStore(true /* supplyService */);
290 
291         final Blob b = new Blob();
292         b.data = TEST_BLOB_DATA;
293         final String l2Key = "fakeKey";
294 
295         // enqueue multiple ipms requests
296         mStore.storeNetworkAttributes(l2Key, TEST_NETWORK_ATTRIBUTES,
297                 status -> assertTrue("Store not successful : " + status.resultCode,
298                         status.isSuccess()));
299         mStore.retrieveNetworkAttributes(l2Key,
300                 (status, key, attr) -> {
301                     throw new RuntimeException("retrieveNetworkAttributes test");
302                 });
303         mStore.storeBlob(l2Key, TEST_CLIENT_ID, TEST_DATA_NAME, b,
304                 status -> {
305                     throw new RuntimeException("storeBlob test");
306                 });
307         mStore.retrieveBlob(l2Key, TEST_CLIENT_ID, TEST_OTHER_DATA_NAME,
308                 (status, key, name, data) -> {
309                     assertTrue("Retrieve blob status not successful : " + status.resultCode,
310                             status.isSuccess());
311                     assertEquals(l2Key, key);
312                     assertEquals(name, TEST_DATA_NAME);
313                     assertTrue(Arrays.equals(b.data, data.data));
314                 });
315 
316         // verify the rest of the queue is still processed in order even if when one or more
317         // callback throw the remote exception
318         InOrder inOrder = inOrder(mMockService);
319 
320         inOrder.verify(mMockService).storeNetworkAttributes(eq(l2Key), mNapCaptor.capture(),
321                 any());
322         inOrder.verify(mMockService).retrieveNetworkAttributes(eq(l2Key), any());
323         inOrder.verify(mMockService).storeBlob(eq(l2Key), eq(TEST_CLIENT_ID), eq(TEST_DATA_NAME),
324                 eq(b), any());
325         inOrder.verify(mMockService).retrieveBlob(eq(l2Key), eq(TEST_CLIENT_ID),
326                 eq(TEST_OTHER_DATA_NAME), any());
327         assertEquals(TEST_NETWORK_ATTRIBUTES, new NetworkAttributes(mNapCaptor.getValue()));
328     }
329 
330     @Test
testFactoryReset()331     public void testFactoryReset() throws RemoteException {
332         startIpMemoryStore(true /* supplyService */);
333         mStore.factoryReset();
334         verify(mMockService, times(1)).factoryReset();
335     }
336 }
337