1 /*
2  * Copyright (C) 2015 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.security.net.config;
18 
19 import android.content.Context;
20 import android.content.pm.ApplicationInfo;
21 import android.test.AndroidTestCase;
22 import android.test.MoreAsserts;
23 import android.util.ArraySet;
24 import android.util.Pair;
25 import java.io.IOException;
26 import java.net.InetAddress;
27 import java.net.Socket;
28 import java.net.URL;
29 import java.security.KeyStore;
30 import java.security.Provider;
31 import java.security.Security;
32 import java.security.cert.X509Certificate;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.Set;
36 import javax.net.ssl.HttpsURLConnection;
37 import javax.net.ssl.SSLContext;
38 import javax.net.ssl.SSLHandshakeException;
39 import javax.net.ssl.SSLSocket;
40 import javax.net.ssl.TrustManager;
41 import javax.net.ssl.TrustManagerFactory;
42 
43 public class XmlConfigTests extends AndroidTestCase {
44 
45     private final static String DEBUG_CA_SUBJ = "O=AOSP, CN=Test debug CA";
46 
testEmptyConfigFile()47     public void testEmptyConfigFile() throws Exception {
48         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.empty_config,
49                 TestUtils.makeApplicationInfo());
50         ApplicationConfig appConfig = new ApplicationConfig(source);
51         assertFalse(appConfig.hasPerDomainConfigs());
52         NetworkSecurityConfig config = appConfig.getConfigForHostname("");
53         assertNotNull(config);
54         // Check defaults.
55         assertTrue(config.isCleartextTrafficPermitted());
56         assertFalse(config.isHstsEnforced());
57         assertFalse(config.getTrustAnchors().isEmpty());
58         PinSet pinSet = config.getPins();
59         assertTrue(pinSet.pins.isEmpty());
60         // Try some connections.
61         SSLContext context = TestUtils.getSSLContext(source);
62         TestUtils.assertConnectionSucceeds(context, "android.com", 443);
63         TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
64         TestUtils.assertUrlConnectionSucceeds(context, "google.com", 443);
65     }
66 
testEmptyAnchors()67     public void testEmptyAnchors() throws Exception {
68         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.empty_trust,
69                 TestUtils.makeApplicationInfo());
70         ApplicationConfig appConfig = new ApplicationConfig(source);
71         assertFalse(appConfig.hasPerDomainConfigs());
72         NetworkSecurityConfig config = appConfig.getConfigForHostname("");
73         assertNotNull(config);
74         // Check defaults.
75         assertTrue(config.isCleartextTrafficPermitted());
76         assertFalse(config.isHstsEnforced());
77         assertTrue(config.getTrustAnchors().isEmpty());
78         PinSet pinSet = config.getPins();
79         assertTrue(pinSet.pins.isEmpty());
80         SSLContext context = TestUtils.getSSLContext(source);
81         TestUtils.assertConnectionFails(context, "android.com", 443);
82         TestUtils.assertConnectionFails(context, "developer.android.com", 443);
83         TestUtils.assertUrlConnectionFails(context, "google.com", 443);
84     }
85 
testBasicDomainConfig()86     public void testBasicDomainConfig() throws Exception {
87         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.domain1,
88                 TestUtils.makeApplicationInfo());
89         ApplicationConfig appConfig = new ApplicationConfig(source);
90         assertTrue(appConfig.hasPerDomainConfigs());
91         NetworkSecurityConfig config = appConfig.getConfigForHostname("");
92         assertNotNull(config);
93         // Check defaults.
94         assertTrue(config.isCleartextTrafficPermitted());
95         assertFalse(config.isHstsEnforced());
96         assertTrue(config.getTrustAnchors().isEmpty());
97         PinSet pinSet = config.getPins();
98         assertTrue(pinSet.pins.isEmpty());
99         // Check android.com.
100         config = appConfig.getConfigForHostname("android.com");
101         assertTrue(config.isCleartextTrafficPermitted());
102         assertFalse(config.isHstsEnforced());
103         assertFalse(config.getTrustAnchors().isEmpty());
104         pinSet = config.getPins();
105         assertTrue(pinSet.pins.isEmpty());
106         // Try connections.
107         SSLContext context = TestUtils.getSSLContext(source);
108         TestUtils.assertConnectionSucceeds(context, "android.com", 443);
109         TestUtils.assertConnectionFails(context, "developer.android.com", 443);
110         TestUtils.assertUrlConnectionFails(context, "google.com", 443);
111         TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
112         // Check that sockets created without the hostname fail with per-domain configs
113         SSLSocket socket = (SSLSocket) context.getSocketFactory()
114                 .createSocket(InetAddress.getByName("android.com"), 443);
115         try {
116         socket.startHandshake();
117         socket.getInputStream();
118         fail();
119         } catch (IOException expected) {
120         }
121     }
122 
testBasicPinning()123     public void testBasicPinning() throws Exception {
124         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.pins1,
125                 TestUtils.makeApplicationInfo());
126         ApplicationConfig appConfig = new ApplicationConfig(source);
127         assertTrue(appConfig.hasPerDomainConfigs());
128         // Check android.com.
129         NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
130         PinSet pinSet = config.getPins();
131         assertFalse(pinSet.pins.isEmpty());
132         // Try connections.
133         SSLContext context = TestUtils.getSSLContext(source);
134         TestUtils.assertConnectionSucceeds(context, "android.com", 443);
135         TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
136         TestUtils.assertConnectionSucceeds(context, "google.com", 443);
137     }
138 
testExpiredPin()139     public void testExpiredPin() throws Exception {
140         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.expired_pin,
141                 TestUtils.makeApplicationInfo());
142         ApplicationConfig appConfig = new ApplicationConfig(source);
143         assertTrue(appConfig.hasPerDomainConfigs());
144         // Check android.com.
145         NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
146         PinSet pinSet = config.getPins();
147         assertFalse(pinSet.pins.isEmpty());
148         // Try connections.
149         SSLContext context = TestUtils.getSSLContext(source);
150         TestUtils.assertConnectionSucceeds(context, "android.com", 443);
151         TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
152     }
153 
testOverridesPins()154     public void testOverridesPins() throws Exception {
155         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.override_pins,
156                 TestUtils.makeApplicationInfo());
157         ApplicationConfig appConfig = new ApplicationConfig(source);
158         assertTrue(appConfig.hasPerDomainConfigs());
159         // Check android.com.
160         NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
161         PinSet pinSet = config.getPins();
162         assertFalse(pinSet.pins.isEmpty());
163         // Try connections.
164         SSLContext context = TestUtils.getSSLContext(source);
165         TestUtils.assertConnectionSucceeds(context, "android.com", 443);
166         TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
167     }
168 
testBadPin()169     public void testBadPin() throws Exception {
170         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.bad_pin,
171                 TestUtils.makeApplicationInfo());
172         ApplicationConfig appConfig = new ApplicationConfig(source);
173         assertTrue(appConfig.hasPerDomainConfigs());
174         // Check android.com.
175         NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
176         PinSet pinSet = config.getPins();
177         assertFalse(pinSet.pins.isEmpty());
178         // Try connections.
179         SSLContext context = TestUtils.getSSLContext(source);
180         TestUtils.assertConnectionFails(context, "android.com", 443);
181         TestUtils.assertUrlConnectionFails(context, "android.com", 443);
182         TestUtils.assertConnectionSucceeds(context, "google.com", 443);
183     }
184 
testMultipleDomains()185     public void testMultipleDomains() throws Exception {
186         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.multiple_domains,
187                 TestUtils.makeApplicationInfo());
188         ApplicationConfig appConfig = new ApplicationConfig(source);
189         assertTrue(appConfig.hasPerDomainConfigs());
190         NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
191         assertTrue(config.isCleartextTrafficPermitted());
192         assertFalse(config.isHstsEnforced());
193         assertFalse(config.getTrustAnchors().isEmpty());
194         PinSet pinSet = config.getPins();
195         assertTrue(pinSet.pins.isEmpty());
196         // Both android.com and google.com should use the same config
197         NetworkSecurityConfig other = appConfig.getConfigForHostname("google.com");
198         assertEquals(config, other);
199         // Try connections.
200         SSLContext context = TestUtils.getSSLContext(source);
201         TestUtils.assertConnectionSucceeds(context, "android.com", 443);
202         TestUtils.assertConnectionSucceeds(context, "google.com", 443);
203         TestUtils.assertConnectionFails(context, "developer.android.com", 443);
204         TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
205     }
206 
testMultipleDomainConfigs()207     public void testMultipleDomainConfigs() throws Exception {
208         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.multiple_configs,
209                 TestUtils.makeApplicationInfo());
210         ApplicationConfig appConfig = new ApplicationConfig(source);
211         assertTrue(appConfig.hasPerDomainConfigs());
212         // Should be two different config objects
213         NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
214         NetworkSecurityConfig other = appConfig.getConfigForHostname("google.com");
215         MoreAsserts.assertNotEqual(config, other);
216         // Try connections.
217         SSLContext context = TestUtils.getSSLContext(source);
218         TestUtils.assertConnectionSucceeds(context, "android.com", 443);
219         TestUtils.assertConnectionSucceeds(context, "google.com", 443);
220         TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
221     }
222 
testIncludeSubdomains()223     public void testIncludeSubdomains() throws Exception {
224         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.subdomains,
225                 TestUtils.makeApplicationInfo());
226         ApplicationConfig appConfig = new ApplicationConfig(source);
227         assertTrue(appConfig.hasPerDomainConfigs());
228         // Try connections.
229         SSLContext context = TestUtils.getSSLContext(source);
230         TestUtils.assertConnectionSucceeds(context, "android.com", 443);
231         TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
232         TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
233         TestUtils.assertUrlConnectionSucceeds(context, "developer.android.com", 443);
234         TestUtils.assertConnectionFails(context, "google.com", 443);
235     }
236 
testAttributes()237     public void testAttributes() throws Exception {
238         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.attributes,
239                 TestUtils.makeApplicationInfo());
240         ApplicationConfig appConfig = new ApplicationConfig(source);
241         assertFalse(appConfig.hasPerDomainConfigs());
242         NetworkSecurityConfig config = appConfig.getConfigForHostname("");
243         assertTrue(config.isHstsEnforced());
244         assertFalse(config.isCleartextTrafficPermitted());
245     }
246 
testResourcePemCertificateSource()247     public void testResourcePemCertificateSource() throws Exception {
248         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.resource_anchors_pem,
249                 TestUtils.makeApplicationInfo());
250         ApplicationConfig appConfig = new ApplicationConfig(source);
251         // Check android.com.
252         NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
253         assertTrue(config.isCleartextTrafficPermitted());
254         assertFalse(config.isHstsEnforced());
255         assertEquals(2, config.getTrustAnchors().size());
256         // Try connections.
257         SSLContext context = TestUtils.getSSLContext(source);
258         TestUtils.assertConnectionSucceeds(context, "android.com", 443);
259         TestUtils.assertConnectionFails(context, "developer.android.com", 443);
260         TestUtils.assertUrlConnectionFails(context, "google.com", 443);
261         TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
262     }
263 
testResourceDerCertificateSource()264     public void testResourceDerCertificateSource() throws Exception {
265         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.resource_anchors_der,
266                 TestUtils.makeApplicationInfo());
267         ApplicationConfig appConfig = new ApplicationConfig(source);
268         // Check android.com.
269         NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
270         assertTrue(config.isCleartextTrafficPermitted());
271         assertFalse(config.isHstsEnforced());
272         assertEquals(2, config.getTrustAnchors().size());
273         // Try connections.
274         SSLContext context = TestUtils.getSSLContext(source);
275         TestUtils.assertConnectionSucceeds(context, "android.com", 443);
276         TestUtils.assertConnectionFails(context, "developer.android.com", 443);
277         TestUtils.assertUrlConnectionFails(context, "google.com", 443);
278         TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
279     }
280 
testNestedDomainConfigs()281     public void testNestedDomainConfigs() throws Exception {
282         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.nested_domains,
283                 TestUtils.makeApplicationInfo());
284         ApplicationConfig appConfig = new ApplicationConfig(source);
285         assertTrue(appConfig.hasPerDomainConfigs());
286         NetworkSecurityConfig parent = appConfig.getConfigForHostname("android.com");
287         NetworkSecurityConfig child = appConfig.getConfigForHostname("developer.android.com");
288         MoreAsserts.assertNotEqual(parent, child);
289         MoreAsserts.assertEmpty(parent.getPins().pins);
290         MoreAsserts.assertNotEmpty(child.getPins().pins);
291         // Check that the child inherited the cleartext value and anchors.
292         assertFalse(child.isCleartextTrafficPermitted());
293         MoreAsserts.assertNotEmpty(child.getTrustAnchors());
294         // Test connections.
295         SSLContext context = TestUtils.getSSLContext(source);
296         TestUtils.assertConnectionSucceeds(context, "android.com", 443);
297         TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
298     }
299 
testNestedDomainConfigsOverride()300     public void testNestedDomainConfigsOverride() throws Exception {
301         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.nested_domains_override,
302                 TestUtils.makeApplicationInfo());
303         ApplicationConfig appConfig = new ApplicationConfig(source);
304         assertTrue(appConfig.hasPerDomainConfigs());
305         NetworkSecurityConfig parent = appConfig.getConfigForHostname("android.com");
306         NetworkSecurityConfig child = appConfig.getConfigForHostname("developer.android.com");
307         MoreAsserts.assertNotEqual(parent, child);
308         assertTrue(parent.isCleartextTrafficPermitted());
309         assertFalse(child.isCleartextTrafficPermitted());
310     }
311 
testDebugOverridesDisabled()312     public void testDebugOverridesDisabled() throws Exception {
313         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_basic,
314                 TestUtils.makeApplicationInfo());
315         ApplicationConfig appConfig = new ApplicationConfig(source);
316         NetworkSecurityConfig config = appConfig.getConfigForHostname("");
317         Set<TrustAnchor> anchors = config.getTrustAnchors();
318         MoreAsserts.assertEmpty(anchors);
319         SSLContext context = TestUtils.getSSLContext(source);
320         TestUtils.assertConnectionFails(context, "android.com", 443);
321         TestUtils.assertConnectionFails(context, "developer.android.com", 443);
322     }
323 
testBasicDebugOverrides()324     public void testBasicDebugOverrides() throws Exception {
325         ApplicationInfo info = TestUtils.makeApplicationInfo();
326         info.flags |= ApplicationInfo.FLAG_DEBUGGABLE;
327         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_basic, info);
328         ApplicationConfig appConfig = new ApplicationConfig(source);
329         NetworkSecurityConfig config = appConfig.getConfigForHostname("");
330         Set<TrustAnchor> anchors = config.getTrustAnchors();
331         MoreAsserts.assertNotEmpty(anchors);
332         for (TrustAnchor anchor : anchors) {
333             assertTrue(anchor.overridesPins);
334         }
335         SSLContext context = TestUtils.getSSLContext(source);
336         TestUtils.assertConnectionSucceeds(context, "android.com", 443);
337         TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
338     }
339 
testDebugOverridesWithDomain()340     public void testDebugOverridesWithDomain() throws Exception {
341         ApplicationInfo info = TestUtils.makeApplicationInfo();
342         info.flags |= ApplicationInfo.FLAG_DEBUGGABLE;
343         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_domain, info);
344         ApplicationConfig appConfig = new ApplicationConfig(source);
345         NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
346         Set<TrustAnchor> anchors = config.getTrustAnchors();
347         boolean foundDebugCA = false;
348         for (TrustAnchor anchor : anchors) {
349             if (anchor.certificate.getSubjectDN().toString().equals(DEBUG_CA_SUBJ)) {
350                 foundDebugCA = true;
351                 assertTrue(anchor.overridesPins);
352             }
353         }
354         assertTrue(foundDebugCA);
355         SSLContext context = TestUtils.getSSLContext(source);
356         TestUtils.assertConnectionSucceeds(context, "android.com", 443);
357         TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
358     }
359 
testDebugInherit()360     public void testDebugInherit() throws Exception {
361         ApplicationInfo info = TestUtils.makeApplicationInfo();
362         info.flags |= ApplicationInfo.FLAG_DEBUGGABLE;
363         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_domain, info);
364         ApplicationConfig appConfig = new ApplicationConfig(source);
365         NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
366         Set<TrustAnchor> anchors = config.getTrustAnchors();
367         boolean foundDebugCA = false;
368         for (TrustAnchor anchor : anchors) {
369             if (anchor.certificate.getSubjectDN().toString().equals(DEBUG_CA_SUBJ)) {
370                 foundDebugCA = true;
371                 assertTrue(anchor.overridesPins);
372             }
373         }
374         assertTrue(foundDebugCA);
375         assertTrue(anchors.size() > 1);
376         SSLContext context = TestUtils.getSSLContext(source);
377         TestUtils.assertConnectionSucceeds(context, "android.com", 443);
378         TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
379     }
380 
testBadConfig(int configId)381     private void testBadConfig(int configId) throws Exception {
382         try {
383             XmlConfigSource source = new XmlConfigSource(getContext(), configId,
384                     TestUtils.makeApplicationInfo());
385             ApplicationConfig appConfig = new ApplicationConfig(source);
386             appConfig.getConfigForHostname("android.com");
387             fail("Bad config " + getContext().getResources().getResourceName(configId)
388                     + " did not fail to parse");
389         } catch (RuntimeException e) {
390             MoreAsserts.assertAssignableFrom(XmlConfigSource.ParserException.class,
391                     e.getCause());
392         }
393     }
394 
testBadConfig0()395     public void testBadConfig0() throws Exception {
396         testBadConfig(R.xml.bad_config0);
397     }
398 
testBadConfig1()399     public void testBadConfig1() throws Exception {
400         testBadConfig(R.xml.bad_config1);
401     }
402 
testBadConfig2()403     public void testBadConfig2() throws Exception {
404         testBadConfig(R.xml.bad_config2);
405     }
406 
testBadConfig3()407     public void testBadConfig3() throws Exception {
408         testBadConfig(R.xml.bad_config3);
409     }
410 
testBadConfig4()411     public void testBadConfig4() throws Exception {
412         testBadConfig(R.xml.bad_config4);
413     }
414 
testBadConfig5()415     public void testBadConfig5() throws Exception {
416         testBadConfig(R.xml.bad_config4);
417     }
418 
testTrustManagerKeystore()419     public void testTrustManagerKeystore() throws Exception {
420         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.bad_pin,
421                 TestUtils.makeApplicationInfo());
422         ApplicationConfig appConfig = new ApplicationConfig(source);
423         Provider provider = new NetworkSecurityConfigProvider();
424         TrustManagerFactory tmf =
425                 TrustManagerFactory.getInstance("PKIX", provider);
426         KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
427         keystore.load(null);
428         int i = 0;
429         for (X509Certificate cert : SystemCertificateSource.getInstance().getCertificates()) {
430             keystore.setEntry(String.valueOf(i),
431                     new KeyStore.TrustedCertificateEntry(cert),
432                     null);
433             i++;
434         }
435         tmf.init(keystore);
436         TrustManager[] tms = tmf.getTrustManagers();
437         SSLContext context = SSLContext.getInstance("TLS");
438         context.init(null, tms, null);
439         TestUtils.assertConnectionSucceeds(context, "android.com" , 443);
440     }
441 
testDebugDedup()442     public void testDebugDedup() throws Exception {
443         ApplicationInfo info = TestUtils.makeApplicationInfo();
444         info.flags |= ApplicationInfo.FLAG_DEBUGGABLE;
445         XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.override_dedup, info);
446         ApplicationConfig appConfig = new ApplicationConfig(source);
447         assertTrue(appConfig.hasPerDomainConfigs());
448         // Check android.com.
449         NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
450         PinSet pinSet = config.getPins();
451         assertFalse(pinSet.pins.isEmpty());
452         // Check that all TrustAnchors come from the override pins debug source.
453         for (TrustAnchor anchor : config.getTrustAnchors()) {
454             assertTrue(anchor.overridesPins);
455         }
456         // Try connections.
457         SSLContext context = TestUtils.getSSLContext(source);
458         TestUtils.assertConnectionSucceeds(context, "android.com", 443);
459         TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
460     }
461 
testExtraDebugResource()462     public void testExtraDebugResource() throws Exception {
463         ApplicationInfo info = TestUtils.makeApplicationInfo();
464         info.flags |= ApplicationInfo.FLAG_DEBUGGABLE;
465         XmlConfigSource source =
466                 new XmlConfigSource(getContext(), R.xml.extra_debug_resource, info);
467         ApplicationConfig appConfig = new ApplicationConfig(source);
468         assertFalse(appConfig.hasPerDomainConfigs());
469         NetworkSecurityConfig config = appConfig.getConfigForHostname("");
470         MoreAsserts.assertNotEmpty(config.getTrustAnchors());
471 
472         // Check that the _debug file is ignored if debug is false.
473         source = new XmlConfigSource(getContext(), R.xml.extra_debug_resource,
474                 TestUtils.makeApplicationInfo());
475         appConfig = new ApplicationConfig(source);
476         assertFalse(appConfig.hasPerDomainConfigs());
477         config = appConfig.getConfigForHostname("");
478         MoreAsserts.assertEmpty(config.getTrustAnchors());
479     }
480 
testExtraDebugResourceIgnored()481     public void testExtraDebugResourceIgnored() throws Exception {
482         // Verify that parsing the extra debug config resource fails only when debugging is true.
483         XmlConfigSource source =
484                 new XmlConfigSource(getContext(), R.xml.bad_extra_debug_resource,
485                         TestUtils.makeApplicationInfo());
486         ApplicationConfig appConfig = new ApplicationConfig(source);
487         // Force parsing the config file.
488         appConfig.getConfigForHostname("");
489 
490         ApplicationInfo info = TestUtils.makeApplicationInfo();
491         info.flags |= ApplicationInfo.FLAG_DEBUGGABLE;
492         source = new XmlConfigSource(getContext(), R.xml.bad_extra_debug_resource, info);
493         appConfig = new ApplicationConfig(source);
494         try {
495             appConfig.getConfigForHostname("");
496             fail("Bad extra debug resource did not fail to parse");
497         } catch (RuntimeException expected) {
498         }
499     }
500 
testDomainWhitespaceTrimming()501     public void testDomainWhitespaceTrimming() throws Exception {
502         XmlConfigSource source =
503                 new XmlConfigSource(getContext(), R.xml.domain_whitespace,
504                         TestUtils.makeApplicationInfo());
505         ApplicationConfig appConfig = new ApplicationConfig(source);
506         NetworkSecurityConfig defaultConfig = appConfig.getConfigForHostname("");
507         MoreAsserts.assertNotEqual(defaultConfig, appConfig.getConfigForHostname("developer.android.com"));
508         MoreAsserts.assertNotEqual(defaultConfig, appConfig.getConfigForHostname("android.com"));
509         SSLContext context = TestUtils.getSSLContext(source);
510         TestUtils.assertConnectionSucceeds(context, "android.com", 443);
511         TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
512     }
513 }
514