Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 1 | package android.security.net.config; |
| 2 | |
| 3 | import android.content.Context; |
| 4 | import android.content.res.Resources; |
| 5 | import android.content.res.XmlResourceParser; |
Chad Brubaker | 32d2a10 | 2016-02-23 16:01:55 -0800 | [diff] [blame] | 6 | import android.os.Build; |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 7 | import android.util.ArraySet; |
| 8 | import android.util.Base64; |
| 9 | import android.util.Pair; |
Chad Brubaker | 32d2a10 | 2016-02-23 16:01:55 -0800 | [diff] [blame] | 10 | import com.android.internal.annotations.VisibleForTesting; |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 11 | import com.android.internal.util.XmlUtils; |
| 12 | |
| 13 | import org.xmlpull.v1.XmlPullParser; |
| 14 | import org.xmlpull.v1.XmlPullParserException; |
| 15 | |
| 16 | import java.io.IOException; |
| 17 | import java.text.ParseException; |
| 18 | import java.text.SimpleDateFormat; |
| 19 | import java.util.ArrayList; |
| 20 | import java.util.Collection; |
| 21 | import java.util.Date; |
| 22 | import java.util.List; |
| 23 | import java.util.Locale; |
| 24 | import java.util.Set; |
| 25 | |
| 26 | /** |
| 27 | * {@link ConfigSource} based on an XML configuration file. |
| 28 | * |
| 29 | * @hide |
| 30 | */ |
| 31 | public class XmlConfigSource implements ConfigSource { |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 32 | private static final int CONFIG_BASE = 0; |
| 33 | private static final int CONFIG_DOMAIN = 1; |
| 34 | private static final int CONFIG_DEBUG = 2; |
| 35 | |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 36 | private final Object mLock = new Object(); |
| 37 | private final int mResourceId; |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 38 | private final boolean mDebugBuild; |
Chad Brubaker | 32d2a10 | 2016-02-23 16:01:55 -0800 | [diff] [blame] | 39 | private final int mTargetSdkVersion; |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 40 | |
| 41 | private boolean mInitialized; |
| 42 | private NetworkSecurityConfig mDefaultConfig; |
| 43 | private Set<Pair<Domain, NetworkSecurityConfig>> mDomainMap; |
| 44 | private Context mContext; |
| 45 | |
Chad Brubaker | 32d2a10 | 2016-02-23 16:01:55 -0800 | [diff] [blame] | 46 | @VisibleForTesting |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 47 | public XmlConfigSource(Context context, int resourceId) { |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 48 | this(context, resourceId, false); |
| 49 | } |
| 50 | |
Chad Brubaker | 32d2a10 | 2016-02-23 16:01:55 -0800 | [diff] [blame] | 51 | @VisibleForTesting |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 52 | public XmlConfigSource(Context context, int resourceId, boolean debugBuild) { |
Chad Brubaker | 32d2a10 | 2016-02-23 16:01:55 -0800 | [diff] [blame] | 53 | this(context, resourceId, debugBuild, Build.VERSION_CODES.CUR_DEVELOPMENT); |
| 54 | } |
| 55 | |
| 56 | public XmlConfigSource(Context context, int resourceId, boolean debugBuild, |
| 57 | int targetSdkVersion) { |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 58 | mResourceId = resourceId; |
| 59 | mContext = context; |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 60 | mDebugBuild = debugBuild; |
Chad Brubaker | 32d2a10 | 2016-02-23 16:01:55 -0800 | [diff] [blame] | 61 | mTargetSdkVersion = targetSdkVersion; |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 62 | } |
| 63 | |
| 64 | public Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs() { |
| 65 | ensureInitialized(); |
| 66 | return mDomainMap; |
| 67 | } |
| 68 | |
| 69 | public NetworkSecurityConfig getDefaultConfig() { |
| 70 | ensureInitialized(); |
| 71 | return mDefaultConfig; |
| 72 | } |
| 73 | |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 74 | private static final String getConfigString(int configType) { |
| 75 | switch (configType) { |
| 76 | case CONFIG_BASE: |
| 77 | return "base-config"; |
| 78 | case CONFIG_DOMAIN: |
| 79 | return "domain-config"; |
| 80 | case CONFIG_DEBUG: |
| 81 | return "debug-overrides"; |
| 82 | default: |
| 83 | throw new IllegalArgumentException("Unknown config type: " + configType); |
| 84 | } |
| 85 | } |
| 86 | |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 87 | private void ensureInitialized() { |
| 88 | synchronized (mLock) { |
| 89 | if (mInitialized) { |
| 90 | return; |
| 91 | } |
| 92 | try (XmlResourceParser parser = mContext.getResources().getXml(mResourceId)) { |
| 93 | parseNetworkSecurityConfig(parser); |
| 94 | mContext = null; |
| 95 | mInitialized = true; |
| 96 | } catch (Resources.NotFoundException | XmlPullParserException | IOException |
| 97 | | ParserException e) { |
| 98 | throw new RuntimeException("Failed to parse XML configuration from " |
| 99 | + mContext.getResources().getResourceEntryName(mResourceId), e); |
| 100 | } |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | private Pin parsePin(XmlResourceParser parser) |
| 105 | throws IOException, XmlPullParserException, ParserException { |
| 106 | String digestAlgorithm = parser.getAttributeValue(null, "digest"); |
| 107 | if (!Pin.isSupportedDigestAlgorithm(digestAlgorithm)) { |
| 108 | throw new ParserException(parser, "Unsupported pin digest algorithm: " |
| 109 | + digestAlgorithm); |
| 110 | } |
| 111 | if (parser.next() != XmlPullParser.TEXT) { |
| 112 | throw new ParserException(parser, "Missing pin digest"); |
| 113 | } |
| 114 | String digest = parser.getText(); |
| 115 | byte[] decodedDigest = null; |
| 116 | try { |
| 117 | decodedDigest = Base64.decode(digest, 0); |
| 118 | } catch (IllegalArgumentException e) { |
| 119 | throw new ParserException(parser, "Invalid pin digest", e); |
| 120 | } |
| 121 | int expectedLength = Pin.getDigestLength(digestAlgorithm); |
| 122 | if (decodedDigest.length != expectedLength) { |
| 123 | throw new ParserException(parser, "digest length " + decodedDigest.length |
| 124 | + " does not match expected length for " + digestAlgorithm + " of " |
| 125 | + expectedLength); |
| 126 | } |
| 127 | if (parser.next() != XmlPullParser.END_TAG) { |
| 128 | throw new ParserException(parser, "pin contains additional elements"); |
| 129 | } |
| 130 | return new Pin(digestAlgorithm, decodedDigest); |
| 131 | } |
| 132 | |
| 133 | private PinSet parsePinSet(XmlResourceParser parser) |
| 134 | throws IOException, XmlPullParserException, ParserException { |
| 135 | String expirationDate = parser.getAttributeValue(null, "expiration"); |
| 136 | long expirationTimestampMilis = Long.MAX_VALUE; |
| 137 | if (expirationDate != null) { |
| 138 | try { |
| 139 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); |
| 140 | sdf.setLenient(false); |
| 141 | Date date = sdf.parse(expirationDate); |
| 142 | if (date == null) { |
| 143 | throw new ParserException(parser, "Invalid expiration date in pin-set"); |
| 144 | } |
| 145 | expirationTimestampMilis = date.getTime(); |
| 146 | } catch (ParseException e) { |
| 147 | throw new ParserException(parser, "Invalid expiration date in pin-set", e); |
| 148 | } |
| 149 | } |
| 150 | |
| 151 | int outerDepth = parser.getDepth(); |
| 152 | Set<Pin> pins = new ArraySet<>(); |
| 153 | while (XmlUtils.nextElementWithin(parser, outerDepth)) { |
| 154 | String tagName = parser.getName(); |
| 155 | if (tagName.equals("pin")) { |
| 156 | pins.add(parsePin(parser)); |
| 157 | } else { |
| 158 | XmlUtils.skipCurrentTag(parser); |
| 159 | } |
| 160 | } |
| 161 | return new PinSet(pins, expirationTimestampMilis); |
| 162 | } |
| 163 | |
| 164 | private Domain parseDomain(XmlResourceParser parser, Set<String> seenDomains) |
| 165 | throws IOException, XmlPullParserException, ParserException { |
| 166 | boolean includeSubdomains = |
| 167 | parser.getAttributeBooleanValue(null, "includeSubdomains", false); |
| 168 | if (parser.next() != XmlPullParser.TEXT) { |
| 169 | throw new ParserException(parser, "Domain name missing"); |
| 170 | } |
| 171 | String domain = parser.getText().toLowerCase(Locale.US); |
| 172 | if (parser.next() != XmlPullParser.END_TAG) { |
| 173 | throw new ParserException(parser, "domain contains additional elements"); |
| 174 | } |
| 175 | // Domains are matched using a most specific match, so don't allow duplicates. |
| 176 | // includeSubdomains isn't relevant here, both android.com + subdomains and android.com |
| 177 | // match for android.com equally. Do not allow any duplicates period. |
| 178 | if (!seenDomains.add(domain)) { |
| 179 | throw new ParserException(parser, domain + " has already been specified"); |
| 180 | } |
| 181 | return new Domain(domain, includeSubdomains); |
| 182 | } |
| 183 | |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 184 | private CertificatesEntryRef parseCertificatesEntry(XmlResourceParser parser, |
| 185 | boolean defaultOverridePins) |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 186 | throws IOException, XmlPullParserException, ParserException { |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 187 | boolean overridePins = |
| 188 | parser.getAttributeBooleanValue(null, "overridePins", defaultOverridePins); |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 189 | int sourceId = parser.getAttributeResourceValue(null, "src", -1); |
| 190 | String sourceString = parser.getAttributeValue(null, "src"); |
| 191 | CertificateSource source = null; |
| 192 | if (sourceString == null) { |
| 193 | throw new ParserException(parser, "certificates element missing src attribute"); |
| 194 | } |
| 195 | if (sourceId != -1) { |
| 196 | // TODO: Cache ResourceCertificateSources by sourceId |
| 197 | source = new ResourceCertificateSource(sourceId, mContext); |
| 198 | } else if ("system".equals(sourceString)) { |
| 199 | source = SystemCertificateSource.getInstance(); |
| 200 | } else if ("user".equals(sourceString)) { |
| 201 | source = UserCertificateSource.getInstance(); |
| 202 | } else { |
| 203 | throw new ParserException(parser, "Unknown certificates src. " |
| 204 | + "Should be one of system|user|@resourceVal"); |
| 205 | } |
| 206 | XmlUtils.skipCurrentTag(parser); |
| 207 | return new CertificatesEntryRef(source, overridePins); |
| 208 | } |
| 209 | |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 210 | private Collection<CertificatesEntryRef> parseTrustAnchors(XmlResourceParser parser, |
| 211 | boolean defaultOverridePins) |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 212 | throws IOException, XmlPullParserException, ParserException { |
| 213 | int outerDepth = parser.getDepth(); |
| 214 | List<CertificatesEntryRef> anchors = new ArrayList<>(); |
| 215 | while (XmlUtils.nextElementWithin(parser, outerDepth)) { |
| 216 | String tagName = parser.getName(); |
| 217 | if (tagName.equals("certificates")) { |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 218 | anchors.add(parseCertificatesEntry(parser, defaultOverridePins)); |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 219 | } else { |
| 220 | XmlUtils.skipCurrentTag(parser); |
| 221 | } |
| 222 | } |
| 223 | return anchors; |
| 224 | } |
| 225 | |
Chad Brubaker | bd173c2 | 2015-11-06 23:02:37 -0800 | [diff] [blame] | 226 | private List<Pair<NetworkSecurityConfig.Builder, Set<Domain>>> parseConfigEntry( |
| 227 | XmlResourceParser parser, Set<String> seenDomains, |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 228 | NetworkSecurityConfig.Builder parentBuilder, int configType) |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 229 | throws IOException, XmlPullParserException, ParserException { |
Chad Brubaker | bd173c2 | 2015-11-06 23:02:37 -0800 | [diff] [blame] | 230 | List<Pair<NetworkSecurityConfig.Builder, Set<Domain>>> builders = new ArrayList<>(); |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 231 | NetworkSecurityConfig.Builder builder = new NetworkSecurityConfig.Builder(); |
Chad Brubaker | bd173c2 | 2015-11-06 23:02:37 -0800 | [diff] [blame] | 232 | builder.setParent(parentBuilder); |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 233 | Set<Domain> domains = new ArraySet<>(); |
| 234 | boolean seenPinSet = false; |
| 235 | boolean seenTrustAnchors = false; |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 236 | boolean defaultOverridePins = configType == CONFIG_DEBUG; |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 237 | String configName = parser.getName(); |
| 238 | int outerDepth = parser.getDepth(); |
Chad Brubaker | bd173c2 | 2015-11-06 23:02:37 -0800 | [diff] [blame] | 239 | // Add this builder now so that this builder occurs before any of its children. This |
| 240 | // makes the final build pass easier. |
| 241 | builders.add(new Pair<>(builder, domains)); |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 242 | // Parse config attributes. Only set values that are present, config inheritence will |
| 243 | // handle the rest. |
| 244 | for (int i = 0; i < parser.getAttributeCount(); i++) { |
| 245 | String name = parser.getAttributeName(i); |
| 246 | if ("hstsEnforced".equals(name)) { |
| 247 | builder.setHstsEnforced( |
| 248 | parser.getAttributeBooleanValue(i, |
| 249 | NetworkSecurityConfig.DEFAULT_HSTS_ENFORCED)); |
| 250 | } else if ("cleartextTrafficPermitted".equals(name)) { |
| 251 | builder.setCleartextTrafficPermitted( |
| 252 | parser.getAttributeBooleanValue(i, |
| 253 | NetworkSecurityConfig.DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED)); |
| 254 | } |
| 255 | } |
| 256 | // Parse the config elements. |
| 257 | while (XmlUtils.nextElementWithin(parser, outerDepth)) { |
| 258 | String tagName = parser.getName(); |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 259 | if ("domain".equals(tagName)) { |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 260 | if (configType != CONFIG_DOMAIN) { |
| 261 | throw new ParserException(parser, |
| 262 | "domain element not allowed in " + getConfigString(configType)); |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 263 | } |
| 264 | Domain domain = parseDomain(parser, seenDomains); |
| 265 | domains.add(domain); |
| 266 | } else if ("trust-anchors".equals(tagName)) { |
| 267 | if (seenTrustAnchors) { |
| 268 | throw new ParserException(parser, |
| 269 | "Multiple trust-anchor elements not allowed"); |
| 270 | } |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 271 | builder.addCertificatesEntryRefs( |
| 272 | parseTrustAnchors(parser, defaultOverridePins)); |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 273 | seenTrustAnchors = true; |
| 274 | } else if ("pin-set".equals(tagName)) { |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 275 | if (configType != CONFIG_DOMAIN) { |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 276 | throw new ParserException(parser, |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 277 | "pin-set element not allowed in " + getConfigString(configType)); |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 278 | } |
| 279 | if (seenPinSet) { |
| 280 | throw new ParserException(parser, "Multiple pin-set elements not allowed"); |
| 281 | } |
| 282 | builder.setPinSet(parsePinSet(parser)); |
| 283 | seenPinSet = true; |
Chad Brubaker | bd173c2 | 2015-11-06 23:02:37 -0800 | [diff] [blame] | 284 | } else if ("domain-config".equals(tagName)) { |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 285 | if (configType != CONFIG_DOMAIN) { |
Chad Brubaker | bd173c2 | 2015-11-06 23:02:37 -0800 | [diff] [blame] | 286 | throw new ParserException(parser, |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 287 | "Nested domain-config not allowed in " + getConfigString(configType)); |
Chad Brubaker | bd173c2 | 2015-11-06 23:02:37 -0800 | [diff] [blame] | 288 | } |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 289 | builders.addAll(parseConfigEntry(parser, seenDomains, builder, configType)); |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 290 | } else { |
| 291 | XmlUtils.skipCurrentTag(parser); |
| 292 | } |
| 293 | } |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 294 | if (configType == CONFIG_DOMAIN && domains.isEmpty()) { |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 295 | throw new ParserException(parser, "No domain elements in domain-config"); |
| 296 | } |
Chad Brubaker | bd173c2 | 2015-11-06 23:02:37 -0800 | [diff] [blame] | 297 | return builders; |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 298 | } |
| 299 | |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 300 | private void addDebugAnchorsIfNeeded(NetworkSecurityConfig.Builder debugConfigBuilder, |
| 301 | NetworkSecurityConfig.Builder builder) { |
| 302 | if (debugConfigBuilder == null || !debugConfigBuilder.hasCertificatesEntryRefs()) { |
| 303 | return; |
| 304 | } |
| 305 | // Don't add trust anchors if not already present, the builder will inherit the anchors |
| 306 | // from its parent, and that's where the trust anchors should be added. |
| 307 | if (!builder.hasCertificatesEntryRefs()) { |
| 308 | return; |
| 309 | } |
| 310 | |
| 311 | builder.addCertificatesEntryRefs(debugConfigBuilder.getCertificatesEntryRefs()); |
| 312 | } |
| 313 | |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 314 | private void parseNetworkSecurityConfig(XmlResourceParser parser) |
| 315 | throws IOException, XmlPullParserException, ParserException { |
| 316 | Set<String> seenDomains = new ArraySet<>(); |
| 317 | List<Pair<NetworkSecurityConfig.Builder, Set<Domain>>> builders = new ArrayList<>(); |
| 318 | NetworkSecurityConfig.Builder baseConfigBuilder = null; |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 319 | NetworkSecurityConfig.Builder debugConfigBuilder = null; |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 320 | boolean seenDebugOverrides = false; |
| 321 | boolean seenBaseConfig = false; |
| 322 | |
| 323 | XmlUtils.beginDocument(parser, "network-security-config"); |
| 324 | int outerDepth = parser.getDepth(); |
| 325 | while (XmlUtils.nextElementWithin(parser, outerDepth)) { |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 326 | if ("base-config".equals(parser.getName())) { |
| 327 | if (seenBaseConfig) { |
| 328 | throw new ParserException(parser, "Only one base-config allowed"); |
| 329 | } |
| 330 | seenBaseConfig = true; |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 331 | baseConfigBuilder = |
| 332 | parseConfigEntry(parser, seenDomains, null, CONFIG_BASE).get(0).first; |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 333 | } else if ("domain-config".equals(parser.getName())) { |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 334 | builders.addAll( |
| 335 | parseConfigEntry(parser, seenDomains, baseConfigBuilder, CONFIG_DOMAIN)); |
| 336 | } else if ("debug-overrides".equals(parser.getName())) { |
| 337 | if (seenDebugOverrides) { |
| 338 | throw new ParserException(parser, "Only one debug-overrides allowed"); |
| 339 | } |
| 340 | if (mDebugBuild) { |
| 341 | debugConfigBuilder = |
| 342 | parseConfigEntry(parser, seenDomains, null, CONFIG_DEBUG).get(0).first; |
| 343 | } else { |
| 344 | XmlUtils.skipCurrentTag(parser); |
| 345 | } |
| 346 | seenDebugOverrides = true; |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 347 | } else { |
| 348 | XmlUtils.skipCurrentTag(parser); |
| 349 | } |
| 350 | } |
| 351 | |
| 352 | // Use the platform default as the parent of the base config for any values not provided |
| 353 | // there. If there is no base config use the platform default. |
| 354 | NetworkSecurityConfig.Builder platformDefaultBuilder = |
Chad Brubaker | 32d2a10 | 2016-02-23 16:01:55 -0800 | [diff] [blame] | 355 | NetworkSecurityConfig.getDefaultBuilder(mTargetSdkVersion); |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 356 | addDebugAnchorsIfNeeded(debugConfigBuilder, platformDefaultBuilder); |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 357 | if (baseConfigBuilder != null) { |
| 358 | baseConfigBuilder.setParent(platformDefaultBuilder); |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 359 | addDebugAnchorsIfNeeded(debugConfigBuilder, baseConfigBuilder); |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 360 | } else { |
| 361 | baseConfigBuilder = platformDefaultBuilder; |
| 362 | } |
| 363 | // Build the per-domain config mapping. |
| 364 | Set<Pair<Domain, NetworkSecurityConfig>> configs = new ArraySet<>(); |
| 365 | |
| 366 | for (Pair<NetworkSecurityConfig.Builder, Set<Domain>> entry : builders) { |
| 367 | NetworkSecurityConfig.Builder builder = entry.first; |
| 368 | Set<Domain> domains = entry.second; |
Chad Brubaker | bd173c2 | 2015-11-06 23:02:37 -0800 | [diff] [blame] | 369 | // Set the parent of configs that do not have a parent to the base-config. This can |
| 370 | // happen if the base-config comes after a domain-config in the file. |
| 371 | // Note that this is safe with regards to children because of the order that |
| 372 | // parseConfigEntry returns builders, the parent is always before the children. The |
| 373 | // children builders will not have build called until _after_ their parents have their |
| 374 | // parent set so everything is consistent. |
| 375 | if (builder.getParent() == null) { |
| 376 | builder.setParent(baseConfigBuilder); |
| 377 | } |
Chad Brubaker | 08d3620 | 2015-11-09 13:38:51 -0800 | [diff] [blame] | 378 | addDebugAnchorsIfNeeded(debugConfigBuilder, builder); |
Chad Brubaker | 5f96702 | 2015-11-04 23:55:29 -0800 | [diff] [blame] | 379 | NetworkSecurityConfig config = builder.build(); |
| 380 | for (Domain domain : domains) { |
| 381 | configs.add(new Pair<>(domain, config)); |
| 382 | } |
| 383 | } |
| 384 | mDefaultConfig = baseConfigBuilder.build(); |
| 385 | mDomainMap = configs; |
| 386 | } |
| 387 | |
| 388 | public static class ParserException extends Exception { |
| 389 | |
| 390 | public ParserException(XmlPullParser parser, String message, Throwable cause) { |
| 391 | super(message + " at: " + parser.getPositionDescription(), cause); |
| 392 | } |
| 393 | |
| 394 | public ParserException(XmlPullParser parser, String message) { |
| 395 | this(parser, message, null); |
| 396 | } |
| 397 | } |
| 398 | } |