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
17package android.security.net.config;
18
19import android.content.Context;
20import android.content.pm.ApplicationInfo;
21import android.test.AndroidTestCase;
22import android.test.MoreAsserts;
23import android.util.ArraySet;
24import android.util.Pair;
25import java.io.IOException;
26import java.net.InetAddress;
27import java.net.Socket;
28import java.net.URL;
29import java.security.KeyStore;
30import java.security.Provider;
31import java.security.Security;
32import java.security.cert.X509Certificate;
33import java.util.ArrayList;
34import java.util.Collections;
35import java.util.Set;
36import javax.net.ssl.HttpsURLConnection;
37import javax.net.ssl.SSLContext;
38import javax.net.ssl.SSLHandshakeException;
39import javax.net.ssl.SSLSocket;
40import javax.net.ssl.TrustManager;
41import javax.net.ssl.TrustManagerFactory;
42
43public class XmlConfigTests extends AndroidTestCase {
44
45    private final static String DEBUG_CA_SUBJ = "O=AOSP, CN=Test debug CA";
46
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
395    public void testBadConfig0() throws Exception {
396        testBadConfig(R.xml.bad_config0);
397    }
398
399    public void testBadConfig1() throws Exception {
400        testBadConfig(R.xml.bad_config1);
401    }
402
403    public void testBadConfig2() throws Exception {
404        testBadConfig(R.xml.bad_config2);
405    }
406
407    public void testBadConfig3() throws Exception {
408        testBadConfig(R.xml.bad_config3);
409    }
410
411    public void testBadConfig4() throws Exception {
412        testBadConfig(R.xml.bad_config4);
413    }
414
415    public void testBadConfig5() throws Exception {
416        testBadConfig(R.xml.bad_config4);
417    }
418
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
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
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
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
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