Merge "Remove unsupported RS graphics API tests."
diff --git a/cmds/idmap/create.cpp b/cmds/idmap/create.cpp
index 6b2f460..c13d318 100644
--- a/cmds/idmap/create.cpp
+++ b/cmds/idmap/create.cpp
@@ -1,6 +1,6 @@
 #include "idmap.h"
 
-#include <UniquePtr.h>
+#include <memory>
 #include <androidfw/AssetManager.h>
 #include <androidfw/ResourceTypes.h>
 #include <androidfw/ZipFileRO.h>
@@ -15,7 +15,7 @@
 namespace {
     int get_zip_entry_crc(const char *zip_path, const char *entry_name, uint32_t *crc)
     {
-        UniquePtr<ZipFileRO> zip(ZipFileRO::open(zip_path));
+        std::unique_ptr<ZipFileRO> zip(ZipFileRO::open(zip_path));
         if (zip.get() == NULL) {
             return -1;
         }
diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp
index f1b2f9e..6d30f0d 100644
--- a/cmds/idmap/scan.cpp
+++ b/cmds/idmap/scan.cpp
@@ -4,7 +4,7 @@
 
 #include "idmap.h"
 
-#include <UniquePtr.h>
+#include <memory>
 #include <androidfw/ResourceTypes.h>
 #include <androidfw/StreamingZipInflater.h>
 #include <androidfw/ZipFileRO.h>
@@ -120,7 +120,7 @@
 
     int parse_apk(const char *path, const char *target_package_name)
     {
-        UniquePtr<ZipFileRO> zip(ZipFileRO::open(path));
+        std::unique_ptr<ZipFileRO> zip(ZipFileRO::open(path));
         if (zip.get() == NULL) {
             ALOGW("%s: failed to open zip %s\n", __FUNCTION__, path);
             return -1;
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 4a0a49b..0051531 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -764,8 +764,7 @@
             String optionData = nextOptionData();
             if (optionData == null || !isNumber(optionData)) {
                 System.err.println("Error: no USER_ID specified");
-                showUsage();
-                return 1;
+                return showUsage();
             } else {
                 userId = Integer.parseInt(optionData);
             }
@@ -868,8 +867,7 @@
                 }
             } else {
                 System.err.println("Error: unknown option: " + opt);
-                showUsage();
-                return 1;
+                return showUsage();
             }
         }
 
@@ -877,16 +875,14 @@
         final String pkg = nextArg();
         if (pkg == null) {
             System.err.println("Error: no package specified.");
-            showUsage();
-            return 1;
+            return showUsage();
         }
 
         // State to apply; {always|ask|never|undefined}, required
         final String modeString = nextArg();
         if (modeString == null) {
             System.err.println("Error: no app link state specified.");
-            showUsage();
-            return 1;
+            return showUsage();
         }
 
         final int newMode;
@@ -955,8 +951,7 @@
                 }
             } else {
                 System.err.println("Error: unknown option: " + opt);
-                showUsage();
-                return 1;
+                return showUsage();
             }
         }
 
@@ -964,8 +959,7 @@
         final String pkg = nextArg();
         if (pkg == null) {
             System.err.println("Error: no package specified.");
-            showUsage();
-            return 1;
+            return showUsage();
         }
 
         try {
@@ -1370,8 +1364,7 @@
                 String optionData = nextOptionData();
                 if (optionData == null || !isNumber(optionData)) {
                     System.err.println("Error: no USER_ID specified");
-                    showUsage();
-                    return 1;
+                    return showUsage();
                 } else {
                     userId = Integer.parseInt(optionData);
                 }
@@ -1379,8 +1372,7 @@
                 flags |= UserInfo.FLAG_MANAGED_PROFILE;
             } else {
                 System.err.println("Error: unknown option " + opt);
-                showUsage();
-                return 1;
+                return showUsage();
             }
         }
         String arg = nextArg();
@@ -1557,8 +1549,7 @@
         String pkg = nextArg();
         if (pkg == null) {
             System.err.println("Error: no package specified");
-            showUsage();
-            return 1;
+            return showUsage();
         }
 
         if (userId == UserHandle.USER_ALL) {
@@ -1626,8 +1617,7 @@
             String optionData = nextOptionData();
             if (optionData == null || !isNumber(optionData)) {
                 System.err.println("Error: no USER_ID specified");
-                showUsage();
-                return 1;
+                return showUsage();
             } else {
                 userId = Integer.parseInt(optionData);
             }
@@ -1636,8 +1626,7 @@
         String pkg = nextArg();
         if (pkg == null) {
             System.err.println("Error: no package specified");
-            showUsage();
-            return 1;
+            return showUsage();
         }
 
         ClearDataObserver obs = new ClearDataObserver();
@@ -1698,8 +1687,7 @@
             String optionData = nextOptionData();
             if (optionData == null || !isNumber(optionData)) {
                 System.err.println("Error: no USER_ID specified");
-                showUsage();
-                return 1;
+                return showUsage();
             } else {
                 userId = Integer.parseInt(optionData);
             }
@@ -1708,8 +1696,7 @@
         String pkg = nextArg();
         if (pkg == null) {
             System.err.println("Error: no package or component specified");
-            showUsage();
-            return 1;
+            return showUsage();
         }
         ComponentName cn = ComponentName.unflattenFromString(pkg);
         if (cn == null) {
@@ -1747,8 +1734,7 @@
             String optionData = nextOptionData();
             if (optionData == null || !isNumber(optionData)) {
                 System.err.println("Error: no USER_ID specified");
-                showUsage();
-                return 1;
+                return showUsage();
             } else {
                 userId = Integer.parseInt(optionData);
             }
@@ -1757,8 +1743,7 @@
         String pkg = nextArg();
         if (pkg == null) {
             System.err.println("Error: no package or component specified");
-            showUsage();
-            return 1;
+            return showUsage();
         }
         try {
             mPm.setApplicationHiddenSettingAsUser(pkg, state, userId);
@@ -1785,14 +1770,12 @@
         String pkg = nextArg();
         if (pkg == null) {
             System.err.println("Error: no package specified");
-            showUsage();
-            return 1;
+            return showUsage();
         }
         String perm = nextArg();
         if (perm == null) {
             System.err.println("Error: no permission specified");
-            showUsage();
-            return 1;
+            return showUsage();
         }
 
         try {
@@ -1808,8 +1791,7 @@
             return 1;
         } catch (IllegalArgumentException e) {
             System.err.println("Bad argument: " + e.toString());
-            showUsage();
-            return 1;
+            return showUsage();
         } catch (SecurityException e) {
             System.err.println("Operation not allowed: " + e.toString());
             return 1;
@@ -1826,8 +1808,7 @@
             return 1;
         } catch (IllegalArgumentException e) {
             System.err.println("Bad argument: " + e.toString());
-            showUsage();
-            return 1;
+            return showUsage();
         } catch (SecurityException e) {
             System.err.println("Operation not allowed: " + e.toString());
             return 1;
@@ -1838,14 +1819,12 @@
         final String permission = nextArg();
         if (permission == null) {
             System.err.println("Error: no permission specified");
-            showUsage();
-            return 1;
+            return showUsage();
         }
         final String enforcedRaw = nextArg();
         if (enforcedRaw == null) {
             System.err.println("Error: no enforcement specified");
-            showUsage();
-            return 1;
+            return showUsage();
         }
         final boolean enforced = Boolean.parseBoolean(enforcedRaw);
         try {
@@ -1857,8 +1836,7 @@
             return 1;
         } catch (IllegalArgumentException e) {
             System.err.println("Bad argument: " + e.toString());
-            showUsage();
-            return 1;
+            return showUsage();
         } catch (SecurityException e) {
             System.err.println("Operation not allowed: " + e.toString());
             return 1;
@@ -1884,8 +1862,7 @@
         String size = nextArg();
         if (size == null) {
             System.err.println("Error: no size specified");
-            showUsage();
-            return 1;
+            return showUsage();
         }
         int len = size.length();
         long multiplier = 1;
@@ -1899,8 +1876,7 @@
                 multiplier = 1024L*1024L*1024L;
             } else {
                 System.err.println("Invalid suffix: " + c);
-                showUsage();
-                return 1;
+                return showUsage();
             }
             size = size.substring(0, len-1);
         }
@@ -1909,8 +1885,7 @@
             sizeVal = Long.parseLong(size) * multiplier;
         } catch (NumberFormatException e) {
             System.err.println("Error: expected number at: " + size);
-            showUsage();
-            return 1;
+            return showUsage();
         }
         String volumeUuid = nextArg();
         if ("internal".equals(volumeUuid)) {
@@ -1934,8 +1909,7 @@
             return 1;
         } catch (IllegalArgumentException e) {
             System.err.println("Bad argument: " + e.toString());
-            showUsage();
-            return 1;
+            return showUsage();
         } catch (SecurityException e) {
             System.err.println("Operation not allowed: " + e.toString());
             return 1;
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index 0a1ba4d1..1ee60b0 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -42,6 +42,7 @@
         } catch (Exception e) {
             if (e instanceof IllegalArgumentException) {
                 showUsage();
+                System.exit(1);
             }
             Log.e(TAG, "Error", e);
             System.err.println("Error: " + e);
diff --git a/core/java/android/net/http/X509TrustManagerExtensions.java b/core/java/android/net/http/X509TrustManagerExtensions.java
index eb4ceda..7c23c23 100644
--- a/core/java/android/net/http/X509TrustManagerExtensions.java
+++ b/core/java/android/net/http/X509TrustManagerExtensions.java
@@ -18,6 +18,9 @@
 
 import com.android.org.conscrypt.TrustManagerImpl;
 
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
 import java.util.List;
@@ -34,7 +37,11 @@
  */
 public class X509TrustManagerExtensions {
 
-    final TrustManagerImpl mDelegate;
+    private final TrustManagerImpl mDelegate;
+    // Methods to use when mDelegate is not a TrustManagerImpl and duck typing is being used.
+    private final X509TrustManager mTrustManager;
+    private final Method mCheckServerTrusted;
+    private final Method mIsUserAddedCertificate;
 
     /**
      * Constructs a new X509TrustManagerExtensions wrapper.
@@ -45,10 +52,31 @@
     public X509TrustManagerExtensions(X509TrustManager tm) throws IllegalArgumentException {
         if (tm instanceof TrustManagerImpl) {
             mDelegate = (TrustManagerImpl) tm;
-        } else {
-            mDelegate = null;
-            throw new IllegalArgumentException("tm is an instance of " + tm.getClass().getName() +
-                    " which is not a supported type of X509TrustManager");
+            mTrustManager = null;
+            mCheckServerTrusted = null;
+            mIsUserAddedCertificate = null;
+            return;
+        }
+        // Use duck typing if possible.
+        mDelegate = null;
+        mTrustManager = tm;
+        // Check that the hostname aware checkServerTrusted is present.
+        try {
+            mCheckServerTrusted = tm.getClass().getMethod("checkServerTrusted",
+                    X509Certificate[].class,
+                    String.class,
+                    String.class);
+        } catch (NoSuchMethodException e) {
+            throw new IllegalArgumentException("Required method"
+                    + " checkServerTrusted(X509Certificate[], String, String, String) missing");
+        }
+        // Check that isUserAddedCertificate is present.
+        try {
+            mIsUserAddedCertificate = tm.getClass().getMethod("isUserAddedCertificate",
+                    X509Certificate.class);
+        } catch (NoSuchMethodException e) {
+            throw new IllegalArgumentException(
+                    "Required method isUserAddedCertificate(X509Certificate) missing");
         }
     }
 
@@ -64,7 +92,24 @@
      */
     public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType,
                                                     String host) throws CertificateException {
-        return mDelegate.checkServerTrusted(chain, authType, host);
+        if (mDelegate != null) {
+            return mDelegate.checkServerTrusted(chain, authType, host);
+        } else {
+            try {
+                return (List<X509Certificate>) mCheckServerTrusted.invoke(mTrustManager, chain,
+                        authType, host);
+            } catch (IllegalAccessException e) {
+                throw new CertificateException("Failed to call checkServerTrusted", e);
+            } catch (InvocationTargetException e) {
+                if (e.getCause() instanceof CertificateException) {
+                    throw (CertificateException) e.getCause();
+                }
+                if (e.getCause() instanceof RuntimeException) {
+                    throw (RuntimeException) e.getCause();
+                }
+                throw new CertificateException("checkServerTrusted failed", e.getCause());
+            }
+        }
     }
 
     /**
@@ -78,6 +123,20 @@
      * otherwise.
      */
     public boolean isUserAddedCertificate(X509Certificate cert) {
-        return mDelegate.isUserAddedCertificate(cert);
+        if (mDelegate != null) {
+            return mDelegate.isUserAddedCertificate(cert);
+        } else {
+            try {
+                return (Boolean) mIsUserAddedCertificate.invoke(mTrustManager, cert);
+            } catch (IllegalAccessException e) {
+                throw new RuntimeException("Failed to call isUserAddedCertificate", e);
+            } catch (InvocationTargetException e) {
+                if (e.getCause() instanceof RuntimeException) {
+                    throw (RuntimeException) e.getCause();
+                } else {
+                    throw new RuntimeException("isUserAddedCertificate failed", e.getCause());
+                }
+            }
+        }
     }
 }
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 1273772b..1e879f2 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -603,6 +603,32 @@
     }
 
     /**
+     * {@hide}
+     * This will be the new name for writeFileDescriptor, for consistency.
+     **/
+    public final void writeRawFileDescriptor(FileDescriptor val) {
+        nativeWriteFileDescriptor(mNativePtr, val);
+    }
+
+    /**
+     * {@hide}
+     * Write an array of FileDescriptor objects into the Parcel.
+     *
+     * @param value The array of objects to be written.
+     */
+    public final void writeRawFileDescriptorArray(FileDescriptor[] value) {
+        if (value != null) {
+            int N = value.length;
+            writeInt(N);
+            for (int i=0; i<N; i++) {
+                writeRawFileDescriptor(value[i]);
+            }
+        } else {
+            writeInt(-1);
+        }
+    }
+
+    /**
      * Write a byte value into the parcel at the current dataPosition(),
      * growing dataCapacity() if needed.
      */
@@ -1679,6 +1705,41 @@
         return nativeReadFileDescriptor(mNativePtr);
     }
 
+    /**
+     * {@hide}
+     * Read and return a new array of FileDescriptors from the parcel.
+     * @return the FileDescriptor array, or null if the array is null.
+     **/
+    public final FileDescriptor[] createRawFileDescriptorArray() {
+        int N = readInt();
+        if (N < 0) {
+            return null;
+        }
+        FileDescriptor[] f = new FileDescriptor[N];
+        for (int i = 0; i < N; i++) {
+            f[i] = readRawFileDescriptor();
+        }
+        return f;
+    }
+
+    /**
+     * {@hide}
+     * Read an array of FileDescriptors from a parcel.
+     * The passed array must be exactly the length of the array in the parcel.
+     * @return the FileDescriptor array, or null if the array is null.
+     **/
+    public final void readRawFileDescriptorArray(FileDescriptor[] val) {
+        int N = readInt();
+        if (N == val.length) {
+            for (int i=0; i<N; i++) {
+                val[i] = readRawFileDescriptor();
+            }
+        } else {
+            throw new RuntimeException("bad array lengths");
+        }
+    }
+
+
     /*package*/ static native FileDescriptor openFileDescriptor(String file,
             int mode) throws FileNotFoundException;
     /*package*/ static native FileDescriptor dupFileDescriptor(FileDescriptor orig)
diff --git a/core/java/android/security/net/config/ApplicationConfig.java b/core/java/android/security/net/config/ApplicationConfig.java
new file mode 100644
index 0000000..48359d47
--- /dev/null
+++ b/core/java/android/security/net/config/ApplicationConfig.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import android.util.Pair;
+import java.util.Locale;
+import java.util.Set;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * An application's network security configuration.
+ *
+ * <p>{@link #getConfigForHostname(String)} provides a means to obtain network security
+ * configuration to be used for communicating with a specific hostname.</p>
+ *
+ * @hide
+ */
+public final class ApplicationConfig {
+    private static ApplicationConfig sInstance;
+    private static Object sLock = new Object();
+
+    private Set<Pair<Domain, NetworkSecurityConfig>> mConfigs;
+    private NetworkSecurityConfig mDefaultConfig;
+    private X509TrustManager mTrustManager;
+
+    private ConfigSource mConfigSource;
+    private boolean mInitialized;
+    private final Object mLock = new Object();
+
+    public ApplicationConfig(ConfigSource configSource) {
+        mConfigSource = configSource;
+        mInitialized = false;
+    }
+
+    /**
+     * @hide
+     */
+    public boolean hasPerDomainConfigs() {
+        ensureInitialized();
+        return mConfigs != null && !mConfigs.isEmpty();
+    }
+
+    /**
+     * Get the {@link NetworkSecurityConfig} corresponding to the provided hostname.
+     * When matching the most specific matching domain rule will be used, if no match exists
+     * then the default configuration will be returned.
+     *
+     * {@code NetworkSecurityConfig} objects returned by this method can be safely cached for
+     * {@code hostname}. Subsequent calls with the same hostname will always return the same
+     * {@code NetworkSecurityConfig}.
+     *
+     * @return {@link NetworkSecurityConfig} to be used to determine
+     * the network security configuration for connections to {@code hostname}.
+     */
+    public NetworkSecurityConfig getConfigForHostname(String hostname) {
+        ensureInitialized();
+        if (hostname.isEmpty() || mConfigs == null) {
+            return mDefaultConfig;
+        }
+        if (hostname.charAt(0) ==  '.') {
+            throw new IllegalArgumentException("hostname must not begin with a .");
+        }
+        // Domains are case insensitive.
+        hostname = hostname.toLowerCase(Locale.US);
+        // Normalize hostname by removing trailing . if present, all Domain hostnames are
+        // absolute.
+        if (hostname.charAt(hostname.length() - 1) == '.') {
+            hostname = hostname.substring(0, hostname.length() - 1);
+        }
+        // Find the Domain -> NetworkSecurityConfig entry with the most specific matching
+        // Domain entry for hostname.
+        // TODO: Use a smarter data structure for the lookup.
+        Pair<Domain, NetworkSecurityConfig> bestMatch = null;
+        for (Pair<Domain, NetworkSecurityConfig> entry : mConfigs) {
+            Domain domain = entry.first;
+            NetworkSecurityConfig config = entry.second;
+            // Check for an exact match.
+            if (domain.hostname.equals(hostname)) {
+                return config;
+            }
+            // Otherwise check if the Domain includes sub-domains and that the hostname is a
+            // sub-domain of the Domain.
+            if (domain.subdomainsIncluded
+                    && hostname.endsWith(domain.hostname)
+                    && hostname.charAt(hostname.length() - domain.hostname.length() - 1) == '.') {
+                if (bestMatch == null) {
+                    bestMatch = entry;
+                } else if (domain.hostname.length() > bestMatch.first.hostname.length()) {
+                    bestMatch = entry;
+                }
+            }
+        }
+        if (bestMatch != null) {
+            return bestMatch.second;
+        }
+        // If no match was found use the default configuration.
+        return mDefaultConfig;
+    }
+
+    /**
+     * Returns the {@link X509TrustManager} that implements the checking of trust anchors and
+     * certificate pinning based on this configuration.
+     */
+    public X509TrustManager getTrustManager() {
+        ensureInitialized();
+        return mTrustManager;
+    }
+
+    private void ensureInitialized() {
+        synchronized(mLock) {
+            if (mInitialized) {
+                return;
+            }
+            mConfigs = mConfigSource.getPerDomainConfigs();
+            mDefaultConfig = mConfigSource.getDefaultConfig();
+            mConfigSource = null;
+            mTrustManager = new RootTrustManager(this);
+            mInitialized = true;
+        }
+    }
+
+    public static void setDefaultInstance(ApplicationConfig config) {
+        synchronized (sLock) {
+            sInstance = config;
+        }
+    }
+
+    public static ApplicationConfig getDefaultInstance() {
+        synchronized (sLock) {
+            return sInstance;
+        }
+    }
+
+    /** @hide */
+    public static ApplicationConfig getPlatformDefault() {
+        return new ApplicationConfig(new ConfigSource() {
+            @Override
+            public NetworkSecurityConfig getDefaultConfig() {
+                return NetworkSecurityConfig.DEFAULT;
+            }
+            @Override
+            public Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs() {
+                return null;
+            }
+        });
+    }
+}
diff --git a/core/java/android/security/net/config/CertificateSource.java b/core/java/android/security/net/config/CertificateSource.java
new file mode 100644
index 0000000..386354d
--- /dev/null
+++ b/core/java/android/security/net/config/CertificateSource.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import java.util.Set;
+import java.security.cert.X509Certificate;
+
+/** @hide */
+public interface CertificateSource {
+    Set<X509Certificate> getCertificates();
+}
diff --git a/core/java/android/security/net/config/CertificatesEntryRef.java b/core/java/android/security/net/config/CertificatesEntryRef.java
new file mode 100644
index 0000000..2ba38c21
--- /dev/null
+++ b/core/java/android/security/net/config/CertificatesEntryRef.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import android.util.ArraySet;
+import java.util.Set;
+import java.security.cert.X509Certificate;
+
+/** @hide */
+public final class CertificatesEntryRef {
+    private final CertificateSource mSource;
+    private final boolean mOverridesPins;
+
+    public CertificatesEntryRef(CertificateSource source, boolean overridesPins) {
+        mSource = source;
+        mOverridesPins = overridesPins;
+    }
+
+    public Set<TrustAnchor> getTrustAnchors() {
+        // TODO: cache this [but handle mutable sources]
+        Set<TrustAnchor> anchors = new ArraySet<TrustAnchor>();
+        for (X509Certificate cert : mSource.getCertificates()) {
+            anchors.add(new TrustAnchor(cert, mOverridesPins));
+        }
+        return anchors;
+    }
+}
diff --git a/core/java/android/security/net/config/ConfigSource.java b/core/java/android/security/net/config/ConfigSource.java
new file mode 100644
index 0000000..4adf265
--- /dev/null
+++ b/core/java/android/security/net/config/ConfigSource.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import android.util.Pair;
+import java.util.Set;
+
+/** @hide */
+public interface ConfigSource {
+    Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs();
+    NetworkSecurityConfig getDefaultConfig();
+}
diff --git a/core/java/android/security/net/config/Domain.java b/core/java/android/security/net/config/Domain.java
new file mode 100644
index 0000000..5bb727a3
--- /dev/null
+++ b/core/java/android/security/net/config/Domain.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import java.util.Locale;
+/** @hide */
+public final class Domain {
+    /**
+     * Lower case hostname for this domain rule.
+     */
+    public final String hostname;
+
+    /**
+     * Whether this domain includes subdomains.
+     */
+    public final boolean subdomainsIncluded;
+
+    public Domain(String hostname, boolean subdomainsIncluded) {
+        if (hostname == null) {
+            throw new NullPointerException("Hostname must not be null");
+        }
+        this.hostname = hostname.toLowerCase(Locale.US);
+        this.subdomainsIncluded = subdomainsIncluded;
+    }
+
+    @Override
+    public int hashCode() {
+        return hostname.hashCode() ^ (subdomainsIncluded ? 1231 : 1237);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (other == this) {
+            return true;
+        }
+        if (!(other instanceof Domain)) {
+            return false;
+        }
+        Domain otherDomain = (Domain) other;
+        return otherDomain.subdomainsIncluded == this.subdomainsIncluded &&
+                otherDomain.hostname.equals(this.hostname);
+    }
+}
diff --git a/core/java/android/security/net/config/KeyStoreCertificateSource.java b/core/java/android/security/net/config/KeyStoreCertificateSource.java
new file mode 100644
index 0000000..1973ef1
--- /dev/null
+++ b/core/java/android/security/net/config/KeyStoreCertificateSource.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import android.util.ArraySet;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
+import java.util.Enumeration;
+import java.util.Set;
+
+/**
+ * {@link CertificateSource} which provides certificates from trusted certificate entries of a
+ * {@link KeyStore}.
+ */
+class KeyStoreCertificateSource implements CertificateSource {
+    private final Object mLock = new Object();
+    private final KeyStore mKeyStore;
+    private Set<X509Certificate> mCertificates;
+
+    public KeyStoreCertificateSource(KeyStore ks) {
+        mKeyStore = ks;
+    }
+
+    @Override
+    public Set<X509Certificate> getCertificates() {
+        synchronized (mLock) {
+            if (mCertificates != null) {
+                return mCertificates;
+            }
+            try {
+                Set<X509Certificate> certificates = new ArraySet<>(mKeyStore.size());
+                for (Enumeration<String> en = mKeyStore.aliases(); en.hasMoreElements();) {
+                    String alias = en.nextElement();
+                    if (!mKeyStore.isCertificateEntry(alias)) {
+                        continue;
+                    }
+                    X509Certificate cert = (X509Certificate) mKeyStore.getCertificate(alias);
+                    if (cert != null) {
+                        certificates.add(cert);
+                    }
+                }
+                mCertificates = certificates;
+                return mCertificates;
+            } catch (KeyStoreException e) {
+                throw new RuntimeException("Failed to load certificates from KeyStore", e);
+            }
+        }
+    }
+}
diff --git a/core/java/android/security/net/config/KeyStoreConfigSource.java b/core/java/android/security/net/config/KeyStoreConfigSource.java
new file mode 100644
index 0000000..8d4f098
--- /dev/null
+++ b/core/java/android/security/net/config/KeyStoreConfigSource.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import android.util.Pair;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.util.Set;
+
+/**
+ * {@link ConfigSource} with a single default config based on a {@link KeyStore} and no per domain
+ * configs.
+ */
+class KeyStoreConfigSource implements ConfigSource {
+    private final NetworkSecurityConfig mConfig;
+
+    public KeyStoreConfigSource(KeyStore ks) {
+        mConfig = new NetworkSecurityConfig.Builder()
+                .addCertificatesEntryRef(
+                        // Use the KeyStore and do not override pins (of which there are none).
+                        new CertificatesEntryRef(new KeyStoreCertificateSource(ks), false))
+                .build();
+    }
+
+    @Override
+    public Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs() {
+        return null;
+    }
+
+    @Override
+    public NetworkSecurityConfig getDefaultConfig() {
+        return mConfig;
+    }
+}
+
diff --git a/core/java/android/security/net/config/NetworkSecurityConfig.java b/core/java/android/security/net/config/NetworkSecurityConfig.java
new file mode 100644
index 0000000..9eab80c
--- /dev/null
+++ b/core/java/android/security/net/config/NetworkSecurityConfig.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * @hide
+ */
+public final class NetworkSecurityConfig {
+    /** @hide */
+    public static final boolean DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED = true;
+    /** @hide */
+    public static final boolean DEFAULT_HSTS_ENFORCED = false;
+    public static final NetworkSecurityConfig DEFAULT = getDefaultBuilder().build();
+
+    private final boolean mCleartextTrafficPermitted;
+    private final boolean mHstsEnforced;
+    private final PinSet mPins;
+    private final List<CertificatesEntryRef> mCertificatesEntryRefs;
+    private Set<TrustAnchor> mAnchors;
+    private final Object mAnchorsLock = new Object();
+    private NetworkSecurityTrustManager mTrustManager;
+    private final Object mTrustManagerLock = new Object();
+
+    private NetworkSecurityConfig(boolean cleartextTrafficPermitted, boolean hstsEnforced,
+            PinSet pins, List<CertificatesEntryRef> certificatesEntryRefs) {
+        mCleartextTrafficPermitted = cleartextTrafficPermitted;
+        mHstsEnforced = hstsEnforced;
+        mPins = pins;
+        mCertificatesEntryRefs = certificatesEntryRefs;
+    }
+
+    public Set<TrustAnchor> getTrustAnchors() {
+        synchronized (mAnchorsLock) {
+            if (mAnchors != null) {
+                return mAnchors;
+            }
+            // Merge trust anchors based on the X509Certificate.
+            // If we see the same certificate in two TrustAnchors, one with overridesPins and one
+            // without, the one with overridesPins wins.
+            Map<X509Certificate, TrustAnchor> anchorMap = new ArrayMap<>();
+            for (CertificatesEntryRef ref : mCertificatesEntryRefs) {
+                Set<TrustAnchor> anchors = ref.getTrustAnchors();
+                for (TrustAnchor anchor : anchors) {
+                    if (anchor.overridesPins) {
+                        anchorMap.put(anchor.certificate, anchor);
+                    } else if (!anchorMap.containsKey(anchor.certificate)) {
+                        anchorMap.put(anchor.certificate, anchor);
+                    }
+                }
+            }
+            ArraySet<TrustAnchor> anchors = new ArraySet<TrustAnchor>(anchorMap.size());
+            anchors.addAll(anchorMap.values());
+            mAnchors = anchors;
+            return mAnchors;
+        }
+    }
+
+    public boolean isCleartextTrafficPermitted() {
+        return mCleartextTrafficPermitted;
+    }
+
+    public boolean isHstsEnforced() {
+        return mHstsEnforced;
+    }
+
+    public PinSet getPins() {
+        return mPins;
+    }
+
+    public NetworkSecurityTrustManager getTrustManager() {
+        synchronized(mTrustManagerLock) {
+            if (mTrustManager == null) {
+                mTrustManager = new NetworkSecurityTrustManager(this);
+            }
+            return mTrustManager;
+        }
+    }
+
+    void onTrustStoreChange() {
+        synchronized (mAnchorsLock) {
+            mAnchors = null;
+        }
+    }
+
+    /**
+     * Return a {@link Builder} for the default {@code NetworkSecurityConfig}.
+     *
+     * <p>
+     * The default configuration has the following properties:
+     * <ol>
+     * <li>Cleartext traffic is permitted.</li>
+     * <li>HSTS is not enforced.</li>
+     * <li>No certificate pinning is used.</li>
+     * <li>The system and user added trusted certificate stores are trusted for connections.</li>
+     * </ol>
+     *
+     * @hide
+     */
+    public static final Builder getDefaultBuilder() {
+        return new Builder()
+                .setCleartextTrafficPermitted(DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED)
+                .setHstsEnforced(DEFAULT_HSTS_ENFORCED)
+                // System certificate store, does not bypass static pins.
+                .addCertificatesEntryRef(
+                        new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+                // User certificate store, does not bypass static pins.
+                .addCertificatesEntryRef(
+                        new CertificatesEntryRef(UserCertificateSource.getInstance(), false));
+    }
+
+    /**
+     * Builder for creating {@code NetworkSecurityConfig} objects.
+     * @hide
+     */
+    public static final class Builder {
+        private List<CertificatesEntryRef> mCertificatesEntryRefs;
+        private PinSet mPinSet;
+        private boolean mCleartextTrafficPermitted = DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED;
+        private boolean mHstsEnforced = DEFAULT_HSTS_ENFORCED;
+        private boolean mCleartextTrafficPermittedSet = false;
+        private boolean mHstsEnforcedSet = false;
+        private Builder mParentBuilder;
+
+        /**
+         * Sets the parent {@code Builder} for this {@code Builder}.
+         * The parent will be used to determine values not configured in this {@code Builder}
+         * in {@link Builder#build()}, recursively if needed.
+         */
+        public Builder setParent(Builder parent) {
+            // Sanity check to avoid adding loops.
+            Builder current = parent;
+            while (current != null) {
+                if (current == this) {
+                    throw new IllegalArgumentException("Loops are not allowed in Builder parents");
+                }
+                current = current.getParent();
+            }
+            mParentBuilder = parent;
+            return this;
+        }
+
+        public Builder getParent() {
+            return mParentBuilder;
+        }
+
+        public Builder setPinSet(PinSet pinSet) {
+            mPinSet = pinSet;
+            return this;
+        }
+
+        private PinSet getEffectivePinSet() {
+            if (mPinSet != null) {
+                return mPinSet;
+            }
+            if (mParentBuilder != null) {
+                return mParentBuilder.getEffectivePinSet();
+            }
+            return PinSet.EMPTY_PINSET;
+        }
+
+        public Builder setCleartextTrafficPermitted(boolean cleartextTrafficPermitted) {
+            mCleartextTrafficPermitted = cleartextTrafficPermitted;
+            mCleartextTrafficPermittedSet = true;
+            return this;
+        }
+
+        private boolean getEffectiveCleartextTrafficPermitted() {
+            if (mCleartextTrafficPermittedSet) {
+                return mCleartextTrafficPermitted;
+            }
+            if (mParentBuilder != null) {
+                return mParentBuilder.getEffectiveCleartextTrafficPermitted();
+            }
+            return DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED;
+        }
+
+        public Builder setHstsEnforced(boolean hstsEnforced) {
+            mHstsEnforced = hstsEnforced;
+            mHstsEnforcedSet = true;
+            return this;
+        }
+
+        private boolean getEffectiveHstsEnforced() {
+            if (mHstsEnforcedSet) {
+                return mHstsEnforced;
+            }
+            if (mParentBuilder != null) {
+                return mParentBuilder.getEffectiveHstsEnforced();
+            }
+            return DEFAULT_HSTS_ENFORCED;
+        }
+
+        public Builder addCertificatesEntryRef(CertificatesEntryRef ref) {
+            if (mCertificatesEntryRefs == null) {
+                mCertificatesEntryRefs = new ArrayList<CertificatesEntryRef>();
+            }
+            mCertificatesEntryRefs.add(ref);
+            return this;
+        }
+
+        public Builder addCertificatesEntryRefs(Collection<? extends CertificatesEntryRef> refs) {
+            if (mCertificatesEntryRefs == null) {
+                mCertificatesEntryRefs = new ArrayList<CertificatesEntryRef>();
+            }
+            mCertificatesEntryRefs.addAll(refs);
+            return this;
+        }
+
+        private List<CertificatesEntryRef> getEffectiveCertificatesEntryRefs() {
+            if (mCertificatesEntryRefs != null) {
+                return mCertificatesEntryRefs;
+            }
+            if (mParentBuilder != null) {
+                return mParentBuilder.getEffectiveCertificatesEntryRefs();
+            }
+            return Collections.<CertificatesEntryRef>emptyList();
+        }
+
+        public boolean hasCertificatesEntryRefs() {
+            return mCertificatesEntryRefs != null;
+        }
+
+        List<CertificatesEntryRef> getCertificatesEntryRefs() {
+            return mCertificatesEntryRefs;
+        }
+
+        public NetworkSecurityConfig build() {
+            boolean cleartextPermitted = getEffectiveCleartextTrafficPermitted();
+            boolean hstsEnforced = getEffectiveHstsEnforced();
+            PinSet pinSet = getEffectivePinSet();
+            List<CertificatesEntryRef> entryRefs = getEffectiveCertificatesEntryRefs();
+            return new NetworkSecurityConfig(cleartextPermitted, hstsEnforced, pinSet, entryRefs);
+        }
+    }
+}
diff --git a/core/java/android/security/net/config/NetworkSecurityConfigProvider.java b/core/java/android/security/net/config/NetworkSecurityConfigProvider.java
new file mode 100644
index 0000000..ac762efe
--- /dev/null
+++ b/core/java/android/security/net/config/NetworkSecurityConfigProvider.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
+import java.security.Security;
+import java.security.Provider;
+
+/** @hide */
+public final class NetworkSecurityConfigProvider extends Provider {
+    private static final String LOG_TAG = "NetworkSecurityConfig";
+    private static final String PREFIX =
+            NetworkSecurityConfigProvider.class.getPackage().getName() + ".";
+    public static final String META_DATA_NETWORK_SECURITY_CONFIG =
+            "android.security.net.config";
+    private static final boolean DBG = true;
+
+    public NetworkSecurityConfigProvider() {
+        // TODO: More clever name than this
+        super("AndroidNSSP", 1.0, "Android Network Security Policy Provider");
+        put("TrustManagerFactory.PKIX", PREFIX + "RootTrustManagerFactorySpi");
+        put("Alg.Alias.TrustManagerFactory.X509", "PKIX");
+    }
+
+    public static void install(Context context) {
+        ApplicationInfo info = null;
+        // TODO: This lookup shouldn't be done in the app startup path, it should be done lazily.
+        try {
+            info = context.getPackageManager().getApplicationInfo(context.getPackageName(),
+                    PackageManager.GET_META_DATA);
+        } catch (PackageManager.NameNotFoundException e) {
+            throw new RuntimeException("Failed to look up ApplicationInfo", e);
+        }
+        int configResourceId = 0;
+        if (info != null && info.metaData != null) {
+            configResourceId = info.metaData.getInt(META_DATA_NETWORK_SECURITY_CONFIG);
+        }
+
+        ApplicationConfig config;
+        if (configResourceId != 0) {
+            boolean debugBuild = (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
+            if (DBG) {
+                Log.d(LOG_TAG, "Using Network Security Config from resource "
+                        + context.getResources().getResourceEntryName(configResourceId)
+                        + " debugBuild: " + debugBuild);
+            }
+            ConfigSource source = new XmlConfigSource(context, configResourceId, debugBuild);
+            config = new ApplicationConfig(source);
+        } else {
+            if (DBG) {
+                Log.d(LOG_TAG, "No Network Security Config specified, using platform default");
+            }
+            config = ApplicationConfig.getPlatformDefault();
+        }
+
+        ApplicationConfig.setDefaultInstance(config);
+        int pos = Security.insertProviderAt(new NetworkSecurityConfigProvider(), 1);
+        if (pos != 1) {
+            throw new RuntimeException("Failed to install provider as highest priority provider."
+                    + " Provider was installed at position " + pos);
+        }
+    }
+}
diff --git a/core/java/android/security/net/config/NetworkSecurityTrustManager.java b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
new file mode 100644
index 0000000..7f5b3ca
--- /dev/null
+++ b/core/java/android/security/net/config/NetworkSecurityTrustManager.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import com.android.org.conscrypt.TrustManagerImpl;
+
+import android.util.ArrayMap;
+import java.io.IOException;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.MessageDigest;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * {@link X509TrustManager} that implements the trust anchor and pinning for a
+ * given {@link NetworkSecurityConfig}.
+ * @hide
+ */
+public class NetworkSecurityTrustManager implements X509TrustManager {
+    // TODO: Replace this with a general X509TrustManager and use duck-typing.
+    private final TrustManagerImpl mDelegate;
+    private final NetworkSecurityConfig mNetworkSecurityConfig;
+
+    public NetworkSecurityTrustManager(NetworkSecurityConfig config) {
+        if (config == null) {
+            throw new NullPointerException("config must not be null");
+        }
+        mNetworkSecurityConfig = config;
+        // TODO: Create our own better KeyStoreImpl
+        try {
+            KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
+            store.load(null);
+            int certNum = 0;
+            for (TrustAnchor anchor : mNetworkSecurityConfig.getTrustAnchors()) {
+                store.setEntry(String.valueOf(certNum++),
+                        new KeyStore.TrustedCertificateEntry(anchor.certificate),
+                        null);
+            }
+            mDelegate = new TrustManagerImpl(store);
+        } catch (GeneralSecurityException | IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void checkClientTrusted(X509Certificate[] chain, String authType)
+            throws CertificateException {
+        throw new CertificateException("Client authentication not supported");
+    }
+
+    @Override
+    public void checkServerTrusted(X509Certificate[] certs, String authType)
+            throws CertificateException {
+        checkServerTrusted(certs, authType, null);
+    }
+
+    /**
+     * Hostname aware version of {@link #checkServerTrusted(X509Certificate[], String)}.
+     * This interface is used by conscrypt and android.net.http.X509TrustManagerExtensions do not
+     * modify without modifying those callers.
+     */
+    public List<X509Certificate> checkServerTrusted(X509Certificate[] certs, String authType,
+            String host) throws CertificateException {
+        List<X509Certificate> trustedChain = mDelegate.checkServerTrusted(certs, authType, host);
+        checkPins(trustedChain);
+        return trustedChain;
+    }
+
+    /**
+     * Check if the provided certificate is a user added certificate authority.
+     * This is required by android.net.http.X509TrustManagerExtensions.
+     */
+    public boolean isUserAddedCertificate(X509Certificate cert) {
+        // TODO: Figure out the right way to handle this, and if it is still even used.
+        return false;
+    }
+
+    private void checkPins(List<X509Certificate> chain) throws CertificateException {
+        PinSet pinSet = mNetworkSecurityConfig.getPins();
+        if (pinSet.pins.isEmpty()
+                || System.currentTimeMillis() > pinSet.expirationTime
+                || !isPinningEnforced(chain)) {
+            return;
+        }
+        Set<String> pinAlgorithms = pinSet.getPinAlgorithms();
+        Map<String, MessageDigest> digestMap = new ArrayMap<String, MessageDigest>(
+                pinAlgorithms.size());
+        for (int i = chain.size() - 1; i >= 0 ; i--) {
+            X509Certificate cert = chain.get(i);
+            byte[] encodedSPKI = cert.getPublicKey().getEncoded();
+            for (String algorithm : pinAlgorithms) {
+                MessageDigest md = digestMap.get(algorithm);
+                if (md == null) {
+                    try {
+                        md = MessageDigest.getInstance(algorithm);
+                    } catch (GeneralSecurityException e) {
+                        throw new RuntimeException(e);
+                    }
+                    digestMap.put(algorithm, md);
+                }
+                if (pinSet.pins.contains(new Pin(algorithm, md.digest(encodedSPKI)))) {
+                    return;
+                }
+            }
+        }
+
+        // TODO: Throw a subclass of CertificateException which indicates a pinning failure.
+        throw new CertificateException("Pin verification failed");
+    }
+
+    private boolean isPinningEnforced(List<X509Certificate> chain) throws CertificateException {
+        if (chain.isEmpty()) {
+            return false;
+        }
+        X509Certificate anchorCert = chain.get(chain.size() - 1);
+        TrustAnchor chainAnchor = null;
+        // TODO: faster lookup
+        for (TrustAnchor anchor : mNetworkSecurityConfig.getTrustAnchors()) {
+            if (anchor.certificate.equals(anchorCert)) {
+                chainAnchor = anchor;
+                break;
+            }
+        }
+        if (chainAnchor == null) {
+            throw new CertificateException("Trusted chain does not end in a TrustAnchor");
+        }
+        return !chainAnchor.overridesPins;
+    }
+
+    @Override
+    public X509Certificate[] getAcceptedIssuers() {
+        return new X509Certificate[0];
+    }
+}
diff --git a/core/java/android/security/net/config/Pin.java b/core/java/android/security/net/config/Pin.java
new file mode 100644
index 0000000..94520e2
--- /dev/null
+++ b/core/java/android/security/net/config/Pin.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import java.util.Arrays;
+
+/** @hide */
+public final class Pin {
+    public final String digestAlgorithm;
+    public final byte[] digest;
+
+    private final int mHashCode;
+
+    public Pin(String digestAlgorithm, byte[] digest) {
+        this.digestAlgorithm = digestAlgorithm;
+        this.digest = digest;
+        mHashCode = Arrays.hashCode(digest) ^ digestAlgorithm.hashCode();
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean isSupportedDigestAlgorithm(String algorithm) {
+        // Currently only SHA-256 is supported. SHA-512 if/once Chromium networking stack
+        // supports it.
+        return "SHA-256".equalsIgnoreCase(algorithm);
+    }
+
+    /**
+     * @hide
+     */
+    public static int getDigestLength(String algorithm) {
+        if ("SHA-256".equalsIgnoreCase(algorithm)) {
+            return 32;
+        }
+        throw new IllegalArgumentException("Unsupported digest algorithm: " + algorithm);
+    }
+
+    @Override
+    public int hashCode() {
+        return mHashCode;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof Pin)) {
+            return false;
+        }
+        Pin other = (Pin) obj;
+        if (other.hashCode() != mHashCode) {
+            return false;
+        }
+        if (!Arrays.equals(digest, other.digest)) {
+            return false;
+        }
+        if (!digestAlgorithm.equals(other.digestAlgorithm)) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/core/java/android/security/net/config/PinSet.java b/core/java/android/security/net/config/PinSet.java
new file mode 100644
index 0000000..d3c975e
--- /dev/null
+++ b/core/java/android/security/net/config/PinSet.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import android.util.ArraySet;
+import java.util.Collections;
+import java.util.Set;
+
+/** @hide */
+public final class PinSet {
+    public static final PinSet EMPTY_PINSET =
+            new PinSet(Collections.<Pin>emptySet(), Long.MAX_VALUE);
+    public final long expirationTime;
+    public final Set<Pin> pins;
+
+    public PinSet(Set<Pin> pins, long expirationTime) {
+        if (pins == null) {
+            throw new NullPointerException("pins must not be null");
+        }
+        this.pins = pins;
+        this.expirationTime = expirationTime;
+    }
+
+    Set<String> getPinAlgorithms() {
+        // TODO: Cache this.
+        Set<String> algorithms = new ArraySet<String>();
+        for (Pin pin : pins) {
+            algorithms.add(pin.digestAlgorithm);
+        }
+        return algorithms;
+    }
+}
diff --git a/core/java/android/security/net/config/ResourceCertificateSource.java b/core/java/android/security/net/config/ResourceCertificateSource.java
new file mode 100644
index 0000000..06dd9d4
--- /dev/null
+++ b/core/java/android/security/net/config/ResourceCertificateSource.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import android.content.Context;
+import android.util.ArraySet;
+import libcore.io.IoUtils;
+import java.io.InputStream;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * {@link CertificateSource} based on certificates contained in an application resource file.
+ * @hide
+ */
+public class ResourceCertificateSource implements CertificateSource {
+    private Set<X509Certificate> mCertificates;
+    private final int  mResourceId;
+    private Context mContext;
+    private final Object mLock = new Object();
+
+    public ResourceCertificateSource(int resourceId, Context context) {
+        mResourceId = resourceId;
+        mContext = context.getApplicationContext();
+    }
+
+    @Override
+    public Set<X509Certificate> getCertificates() {
+        synchronized (mLock) {
+            if (mCertificates != null) {
+                return mCertificates;
+            }
+            Set<X509Certificate> certificates = new ArraySet<X509Certificate>();
+            Collection<? extends Certificate> certs;
+            InputStream in = null;
+            try {
+                CertificateFactory factory = CertificateFactory.getInstance("X.509");
+                in = mContext.getResources().openRawResource(mResourceId);
+                certs = factory.generateCertificates(in);
+            } catch (CertificateException e) {
+                throw new RuntimeException("Failed to load trust anchors from id " + mResourceId,
+                        e);
+            } finally {
+                IoUtils.closeQuietly(in);
+            }
+            for (Certificate cert : certs) {
+                    certificates.add((X509Certificate) cert);
+            }
+            mCertificates = certificates;
+            mContext = null;
+            return mCertificates;
+        }
+    }
+}
diff --git a/core/java/android/security/net/config/RootTrustManager.java b/core/java/android/security/net/config/RootTrustManager.java
new file mode 100644
index 0000000..b87bf1f
--- /dev/null
+++ b/core/java/android/security/net/config/RootTrustManager.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.List;
+
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * {@link X509TrustManager} based on an {@link ApplicationConfig}.
+ *
+ * <p>This {@code X509TrustManager} delegates to the specific trust manager for the hostname
+ * being used for the connection (See {@link ApplicationConfig#getConfigForHostname(String)} and
+ * {@link NetworkSecurityTrustManager}).</p>
+ *
+ * Note that if the {@code ApplicationConfig} has per-domain configurations the hostname aware
+ * {@link #checkServerTrusted(X509Certificate[], String String)} must be used instead of the normal
+ * non-aware call.
+ * @hide */
+public class RootTrustManager implements X509TrustManager {
+    private final ApplicationConfig mConfig;
+    private static final X509Certificate[] EMPTY_ISSUERS = new X509Certificate[0];
+
+    public RootTrustManager(ApplicationConfig config) {
+        if (config == null) {
+            throw new NullPointerException("config must not be null");
+        }
+        mConfig = config;
+    }
+
+    @Override
+    public void checkClientTrusted(X509Certificate[] chain, String authType)
+            throws CertificateException {
+        throw new CertificateException("Client authentication not supported");
+    }
+
+    @Override
+    public void checkServerTrusted(X509Certificate[] certs, String authType)
+            throws CertificateException {
+        if (mConfig.hasPerDomainConfigs()) {
+            throw new CertificateException(
+                    "Domain specific configurations require that hostname aware"
+                    + " checkServerTrusted(X509Certificate[], String, String) is used");
+        }
+        NetworkSecurityConfig config = mConfig.getConfigForHostname("");
+        config.getTrustManager().checkServerTrusted(certs, authType);
+    }
+
+    /**
+     * Hostname aware version of {@link #checkServerTrusted(X509Certificate[], String)}.
+     * This interface is used by conscrypt and android.net.http.X509TrustManagerExtensions do not
+     * modify without modifying those callers.
+     */
+    public List<X509Certificate> checkServerTrusted(X509Certificate[] certs, String authType,
+            String hostname) throws CertificateException {
+        NetworkSecurityConfig config = mConfig.getConfigForHostname(hostname);
+        return config.getTrustManager().checkServerTrusted(certs, authType, hostname);
+    }
+
+    /**
+     * Check if the provided certificate is a user added certificate authority.
+     * This is required by android.net.http.X509TrustManagerExtensions.
+     */
+    public boolean isUserAddedCertificate(X509Certificate cert) {
+        // TODO: Figure out the right way to handle this, and if it is still even used.
+        return false;
+    }
+
+    @Override
+    public X509Certificate[] getAcceptedIssuers() {
+        return EMPTY_ISSUERS;
+    }
+}
diff --git a/core/java/android/security/net/config/RootTrustManagerFactorySpi.java b/core/java/android/security/net/config/RootTrustManagerFactorySpi.java
new file mode 100644
index 0000000..0a1fe88
--- /dev/null
+++ b/core/java/android/security/net/config/RootTrustManagerFactorySpi.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import android.util.Pair;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidParameterException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.Security;
+import java.util.Set;
+import javax.net.ssl.ManagerFactoryParameters;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.TrustManagerFactorySpi;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/** @hide */
+public class RootTrustManagerFactorySpi extends TrustManagerFactorySpi {
+    private ApplicationConfig mApplicationConfig;
+    private NetworkSecurityConfig mConfig;
+
+    @Override
+    public void engineInit(ManagerFactoryParameters spec)
+            throws InvalidAlgorithmParameterException {
+        if (!(spec instanceof ApplicationConfigParameters)) {
+            throw new InvalidAlgorithmParameterException("Unsupported spec: " +  spec + ". Only "
+                    + ApplicationConfigParameters.class.getName() + " supported");
+
+        }
+        mApplicationConfig = ((ApplicationConfigParameters) spec).config;
+    }
+
+    @Override
+    public void engineInit(KeyStore ks) throws KeyStoreException {
+        if (ks != null) {
+            mApplicationConfig = new ApplicationConfig(new KeyStoreConfigSource(ks));
+        } else {
+            mApplicationConfig = ApplicationConfig.getDefaultInstance();
+        }
+    }
+
+    @Override
+    public TrustManager[] engineGetTrustManagers() {
+        if (mApplicationConfig == null) {
+            throw new IllegalStateException("TrustManagerFactory not initialized");
+        }
+        return new TrustManager[] { mApplicationConfig.getTrustManager() };
+    }
+
+    @VisibleForTesting
+    public static final class ApplicationConfigParameters implements ManagerFactoryParameters {
+        public final ApplicationConfig config;
+        public ApplicationConfigParameters(ApplicationConfig config) {
+            this.config = config;
+        }
+    }
+}
diff --git a/core/java/android/security/net/config/SystemCertificateSource.java b/core/java/android/security/net/config/SystemCertificateSource.java
new file mode 100644
index 0000000..7649a97
--- /dev/null
+++ b/core/java/android/security/net/config/SystemCertificateSource.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import android.os.Environment;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Set;
+import libcore.io.IoUtils;
+
+/**
+ * {@link CertificateSource} based on the system trusted CA store.
+ * @hide
+ */
+public class SystemCertificateSource implements CertificateSource {
+    private static final SystemCertificateSource INSTANCE = new SystemCertificateSource();
+    private Set<X509Certificate> mSystemCerts = null;
+    private final Object mLock = new Object();
+
+    private SystemCertificateSource() {
+    }
+
+    public static SystemCertificateSource getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public Set<X509Certificate> getCertificates() {
+        // TODO: loading all of these is wasteful, we should instead use a keystore style API.
+        synchronized (mLock) {
+            if (mSystemCerts != null) {
+                return mSystemCerts;
+            }
+            CertificateFactory certFactory;
+            try {
+                certFactory = CertificateFactory.getInstance("X.509");
+            } catch (CertificateException e) {
+                throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
+            }
+
+            final String ANDROID_ROOT = System.getenv("ANDROID_ROOT");
+            final File systemCaDir = new File(ANDROID_ROOT + "/etc/security/cacerts");
+            final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
+            final File userRemovedCaDir = new File(configDir, "cacerts-removed");
+            // Sanity check
+            if (!systemCaDir.isDirectory()) {
+                throw new AssertionError(systemCaDir + " is not a directory");
+            }
+
+            Set<X509Certificate> systemCerts = new ArraySet<X509Certificate>();
+            for (String caFile : systemCaDir.list()) {
+                // Skip any CAs in the user's deleted directory.
+                if (new File(userRemovedCaDir, caFile).exists()) {
+                    continue;
+                }
+                InputStream is = null;
+                try {
+                    is = new BufferedInputStream(
+                            new FileInputStream(new File(systemCaDir, caFile)));
+                    systemCerts.add((X509Certificate) certFactory.generateCertificate(is));
+                } catch (CertificateException | IOException e) {
+                    // Don't rethrow to be consistent with conscrypt's cert loading code.
+                    continue;
+                } finally {
+                    IoUtils.closeQuietly(is);
+                }
+            }
+            mSystemCerts = systemCerts;
+            return mSystemCerts;
+        }
+    }
+
+    public void onCertificateStorageChange() {
+        synchronized (mLock) {
+            mSystemCerts = null;
+        }
+    }
+}
diff --git a/core/java/android/security/net/config/TrustAnchor.java b/core/java/android/security/net/config/TrustAnchor.java
new file mode 100644
index 0000000..b62d85f
--- /dev/null
+++ b/core/java/android/security/net/config/TrustAnchor.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import java.security.cert.X509Certificate;
+
+/** @hide */
+public final class TrustAnchor {
+    public final X509Certificate certificate;
+    public final boolean overridesPins;
+
+    public TrustAnchor(X509Certificate certificate, boolean overridesPins) {
+        if (certificate == null) {
+            throw new NullPointerException("certificate");
+        }
+        this.certificate = certificate;
+        this.overridesPins = overridesPins;
+    }
+}
diff --git a/core/java/android/security/net/config/UserCertificateSource.java b/core/java/android/security/net/config/UserCertificateSource.java
new file mode 100644
index 0000000..e9d5aa1
--- /dev/null
+++ b/core/java/android/security/net/config/UserCertificateSource.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import android.os.Environment;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.Set;
+import libcore.io.IoUtils;
+
+/**
+ * {@link CertificateSource} based on the user-installed trusted CA store.
+ * @hide
+ */
+public class UserCertificateSource implements CertificateSource {
+    private static final UserCertificateSource INSTANCE = new UserCertificateSource();
+    private Set<X509Certificate> mUserCerts = null;
+    private final Object mLock = new Object();
+
+    private UserCertificateSource() {
+    }
+
+    public static UserCertificateSource getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public Set<X509Certificate> getCertificates() {
+        // TODO: loading all of these is wasteful, we should instead use a keystore style API.
+        synchronized (mLock) {
+            if (mUserCerts != null) {
+                return mUserCerts;
+            }
+            CertificateFactory certFactory;
+            try {
+                certFactory = CertificateFactory.getInstance("X.509");
+            } catch (CertificateException e) {
+                throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
+            }
+            final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
+            final File userCaDir = new File(configDir, "cacerts-added");
+            Set<X509Certificate> userCerts = new ArraySet<X509Certificate>();
+            // If the user hasn't added any certificates the directory may not exist.
+            if (userCaDir.isDirectory()) {
+                for (String caFile : userCaDir.list()) {
+                    InputStream is = null;
+                    try {
+                        is = new BufferedInputStream(
+                                new FileInputStream(new File(userCaDir, caFile)));
+                        userCerts.add((X509Certificate) certFactory.generateCertificate(is));
+                    } catch (CertificateException | IOException e) {
+                        // Don't rethrow to be consistent with conscrypt's cert loading code.
+                        continue;
+                    } finally {
+                        IoUtils.closeQuietly(is);
+                    }
+                }
+            }
+            mUserCerts = userCerts;
+            return mUserCerts;
+        }
+    }
+
+    public void onCertificateStorageChange() {
+        synchronized (mLock) {
+            mUserCerts = null;
+        }
+    }
+}
diff --git a/core/java/android/security/net/config/XmlConfigSource.java b/core/java/android/security/net/config/XmlConfigSource.java
new file mode 100644
index 0000000..1706e95
--- /dev/null
+++ b/core/java/android/security/net/config/XmlConfigSource.java
@@ -0,0 +1,387 @@
+package android.security.net.config;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.util.ArraySet;
+import android.util.Base64;
+import android.util.Pair;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * {@link ConfigSource} based on an XML configuration file.
+ *
+ * @hide
+ */
+public class XmlConfigSource implements ConfigSource {
+    private static final int CONFIG_BASE = 0;
+    private static final int CONFIG_DOMAIN = 1;
+    private static final int CONFIG_DEBUG = 2;
+
+    private final Object mLock = new Object();
+    private final int mResourceId;
+    private final boolean mDebugBuild;
+
+    private boolean mInitialized;
+    private NetworkSecurityConfig mDefaultConfig;
+    private Set<Pair<Domain, NetworkSecurityConfig>> mDomainMap;
+    private Context mContext;
+
+    public XmlConfigSource(Context context, int resourceId) {
+        this(context, resourceId, false);
+    }
+
+    public XmlConfigSource(Context context, int resourceId, boolean debugBuild) {
+        mResourceId = resourceId;
+        mContext = context;
+        mDebugBuild = debugBuild;
+    }
+
+    public Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs() {
+        ensureInitialized();
+        return mDomainMap;
+    }
+
+    public NetworkSecurityConfig getDefaultConfig() {
+        ensureInitialized();
+        return mDefaultConfig;
+    }
+
+    private static final String getConfigString(int configType) {
+        switch (configType) {
+            case CONFIG_BASE:
+                return "base-config";
+            case CONFIG_DOMAIN:
+                return "domain-config";
+            case CONFIG_DEBUG:
+                return "debug-overrides";
+            default:
+                throw new IllegalArgumentException("Unknown config type: " + configType);
+        }
+    }
+
+    private void ensureInitialized() {
+        synchronized (mLock) {
+            if (mInitialized) {
+                return;
+            }
+            try (XmlResourceParser parser = mContext.getResources().getXml(mResourceId)) {
+                parseNetworkSecurityConfig(parser);
+                mContext = null;
+                mInitialized = true;
+            } catch (Resources.NotFoundException | XmlPullParserException | IOException
+                    | ParserException e) {
+                throw new RuntimeException("Failed to parse XML configuration from "
+                        + mContext.getResources().getResourceEntryName(mResourceId), e);
+            }
+        }
+    }
+
+    private Pin parsePin(XmlResourceParser parser)
+            throws IOException, XmlPullParserException, ParserException {
+        String digestAlgorithm = parser.getAttributeValue(null, "digest");
+        if (!Pin.isSupportedDigestAlgorithm(digestAlgorithm)) {
+            throw new ParserException(parser, "Unsupported pin digest algorithm: "
+                    + digestAlgorithm);
+        }
+        if (parser.next() != XmlPullParser.TEXT) {
+            throw new ParserException(parser, "Missing pin digest");
+        }
+        String digest = parser.getText();
+        byte[] decodedDigest = null;
+        try {
+            decodedDigest = Base64.decode(digest, 0);
+        } catch (IllegalArgumentException e) {
+            throw new ParserException(parser, "Invalid pin digest", e);
+        }
+        int expectedLength = Pin.getDigestLength(digestAlgorithm);
+        if (decodedDigest.length != expectedLength) {
+            throw new ParserException(parser, "digest length " + decodedDigest.length
+                    + " does not match expected length for " + digestAlgorithm + " of "
+                    + expectedLength);
+        }
+        if (parser.next() != XmlPullParser.END_TAG) {
+            throw new ParserException(parser, "pin contains additional elements");
+        }
+        return new Pin(digestAlgorithm, decodedDigest);
+    }
+
+    private PinSet parsePinSet(XmlResourceParser parser)
+            throws IOException, XmlPullParserException, ParserException {
+        String expirationDate = parser.getAttributeValue(null, "expiration");
+        long expirationTimestampMilis = Long.MAX_VALUE;
+        if (expirationDate != null) {
+            try {
+                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+                sdf.setLenient(false);
+                Date date = sdf.parse(expirationDate);
+                if (date == null) {
+                    throw new ParserException(parser, "Invalid expiration date in pin-set");
+                }
+                expirationTimestampMilis = date.getTime();
+            } catch (ParseException e) {
+                throw new ParserException(parser, "Invalid expiration date in pin-set", e);
+            }
+        }
+
+        int outerDepth = parser.getDepth();
+        Set<Pin> pins = new ArraySet<>();
+        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+            String tagName = parser.getName();
+            if (tagName.equals("pin")) {
+                pins.add(parsePin(parser));
+            } else {
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+        return new PinSet(pins, expirationTimestampMilis);
+    }
+
+    private Domain parseDomain(XmlResourceParser parser, Set<String> seenDomains)
+            throws IOException, XmlPullParserException, ParserException {
+        boolean includeSubdomains =
+                parser.getAttributeBooleanValue(null, "includeSubdomains", false);
+        if (parser.next() != XmlPullParser.TEXT) {
+            throw new ParserException(parser, "Domain name missing");
+        }
+        String domain = parser.getText().toLowerCase(Locale.US);
+        if (parser.next() != XmlPullParser.END_TAG) {
+            throw new ParserException(parser, "domain contains additional elements");
+        }
+        // Domains are matched using a most specific match, so don't allow duplicates.
+        // includeSubdomains isn't relevant here, both android.com + subdomains and android.com
+        // match for android.com equally. Do not allow any duplicates period.
+        if (!seenDomains.add(domain)) {
+            throw new ParserException(parser, domain + " has already been specified");
+        }
+        return new Domain(domain, includeSubdomains);
+    }
+
+    private CertificatesEntryRef parseCertificatesEntry(XmlResourceParser parser,
+            boolean defaultOverridePins)
+            throws IOException, XmlPullParserException, ParserException {
+        boolean overridePins =
+                parser.getAttributeBooleanValue(null, "overridePins", defaultOverridePins);
+        int sourceId = parser.getAttributeResourceValue(null, "src", -1);
+        String sourceString = parser.getAttributeValue(null, "src");
+        CertificateSource source = null;
+        if (sourceString == null) {
+            throw new ParserException(parser, "certificates element missing src attribute");
+        }
+        if (sourceId != -1) {
+            // TODO: Cache ResourceCertificateSources by sourceId
+            source = new ResourceCertificateSource(sourceId, mContext);
+        } else if ("system".equals(sourceString)) {
+            source = SystemCertificateSource.getInstance();
+        } else if ("user".equals(sourceString)) {
+            source = UserCertificateSource.getInstance();
+        } else {
+            throw new ParserException(parser, "Unknown certificates src. "
+                    + "Should be one of system|user|@resourceVal");
+        }
+        XmlUtils.skipCurrentTag(parser);
+        return new CertificatesEntryRef(source, overridePins);
+    }
+
+    private Collection<CertificatesEntryRef> parseTrustAnchors(XmlResourceParser parser,
+            boolean defaultOverridePins)
+            throws IOException, XmlPullParserException, ParserException {
+        int outerDepth = parser.getDepth();
+        List<CertificatesEntryRef> anchors = new ArrayList<>();
+        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+            String tagName = parser.getName();
+            if (tagName.equals("certificates")) {
+                anchors.add(parseCertificatesEntry(parser, defaultOverridePins));
+            } else {
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+        return anchors;
+    }
+
+    private List<Pair<NetworkSecurityConfig.Builder, Set<Domain>>> parseConfigEntry(
+            XmlResourceParser parser, Set<String> seenDomains,
+            NetworkSecurityConfig.Builder parentBuilder, int configType)
+            throws IOException, XmlPullParserException, ParserException {
+        List<Pair<NetworkSecurityConfig.Builder, Set<Domain>>> builders = new ArrayList<>();
+        NetworkSecurityConfig.Builder builder = new NetworkSecurityConfig.Builder();
+        builder.setParent(parentBuilder);
+        Set<Domain> domains = new ArraySet<>();
+        boolean seenPinSet = false;
+        boolean seenTrustAnchors = false;
+        boolean defaultOverridePins = configType == CONFIG_DEBUG;
+        String configName = parser.getName();
+        int outerDepth = parser.getDepth();
+        // Add this builder now so that this builder occurs before any of its children. This
+        // makes the final build pass easier.
+        builders.add(new Pair<>(builder, domains));
+        // Parse config attributes. Only set values that are present, config inheritence will
+        // handle the rest.
+        for (int i = 0; i < parser.getAttributeCount(); i++) {
+            String name = parser.getAttributeName(i);
+            if ("hstsEnforced".equals(name)) {
+                builder.setHstsEnforced(
+                        parser.getAttributeBooleanValue(i,
+                                NetworkSecurityConfig.DEFAULT_HSTS_ENFORCED));
+            } else if ("cleartextTrafficPermitted".equals(name)) {
+                builder.setCleartextTrafficPermitted(
+                        parser.getAttributeBooleanValue(i,
+                                NetworkSecurityConfig.DEFAULT_CLEARTEXT_TRAFFIC_PERMITTED));
+            }
+        }
+        // Parse the config elements.
+        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+            String tagName = parser.getName();
+            if ("domain".equals(tagName)) {
+                if (configType != CONFIG_DOMAIN) {
+                    throw new ParserException(parser,
+                            "domain element not allowed in " + getConfigString(configType));
+                }
+                Domain domain = parseDomain(parser, seenDomains);
+                domains.add(domain);
+            } else if ("trust-anchors".equals(tagName)) {
+                if (seenTrustAnchors) {
+                    throw new ParserException(parser,
+                            "Multiple trust-anchor elements not allowed");
+                }
+                builder.addCertificatesEntryRefs(
+                        parseTrustAnchors(parser, defaultOverridePins));
+                seenTrustAnchors = true;
+            } else if ("pin-set".equals(tagName)) {
+                if (configType != CONFIG_DOMAIN) {
+                    throw new ParserException(parser,
+                            "pin-set element not allowed in " + getConfigString(configType));
+                }
+                if (seenPinSet) {
+                    throw new ParserException(parser, "Multiple pin-set elements not allowed");
+                }
+                builder.setPinSet(parsePinSet(parser));
+                seenPinSet = true;
+            } else if ("domain-config".equals(tagName)) {
+                if (configType != CONFIG_DOMAIN) {
+                    throw new ParserException(parser,
+                            "Nested domain-config not allowed in " + getConfigString(configType));
+                }
+                builders.addAll(parseConfigEntry(parser, seenDomains, builder, configType));
+            } else {
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+        if (configType == CONFIG_DOMAIN && domains.isEmpty()) {
+            throw new ParserException(parser, "No domain elements in domain-config");
+        }
+        return builders;
+    }
+
+    private void addDebugAnchorsIfNeeded(NetworkSecurityConfig.Builder debugConfigBuilder,
+            NetworkSecurityConfig.Builder builder) {
+        if (debugConfigBuilder == null || !debugConfigBuilder.hasCertificatesEntryRefs()) {
+            return;
+        }
+        // Don't add trust anchors if not already present, the builder will inherit the anchors
+        // from its parent, and that's where the trust anchors should be added.
+        if (!builder.hasCertificatesEntryRefs()) {
+            return;
+        }
+
+        builder.addCertificatesEntryRefs(debugConfigBuilder.getCertificatesEntryRefs());
+    }
+
+    private void parseNetworkSecurityConfig(XmlResourceParser parser)
+            throws IOException, XmlPullParserException, ParserException {
+        Set<String> seenDomains = new ArraySet<>();
+        List<Pair<NetworkSecurityConfig.Builder, Set<Domain>>> builders = new ArrayList<>();
+        NetworkSecurityConfig.Builder baseConfigBuilder = null;
+        NetworkSecurityConfig.Builder debugConfigBuilder = null;
+        boolean seenDebugOverrides = false;
+        boolean seenBaseConfig = false;
+
+        XmlUtils.beginDocument(parser, "network-security-config");
+        int outerDepth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+            if ("base-config".equals(parser.getName())) {
+                if (seenBaseConfig) {
+                    throw new ParserException(parser, "Only one base-config allowed");
+                }
+                seenBaseConfig = true;
+                baseConfigBuilder =
+                        parseConfigEntry(parser, seenDomains, null, CONFIG_BASE).get(0).first;
+            } else if ("domain-config".equals(parser.getName())) {
+                builders.addAll(
+                        parseConfigEntry(parser, seenDomains, baseConfigBuilder, CONFIG_DOMAIN));
+            } else if ("debug-overrides".equals(parser.getName())) {
+                if (seenDebugOverrides) {
+                    throw new ParserException(parser, "Only one debug-overrides allowed");
+                }
+                if (mDebugBuild) {
+                    debugConfigBuilder =
+                            parseConfigEntry(parser, seenDomains, null, CONFIG_DEBUG).get(0).first;
+                } else {
+                    XmlUtils.skipCurrentTag(parser);
+                }
+                seenDebugOverrides = true;
+            } else {
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+
+        // Use the platform default as the parent of the base config for any values not provided
+        // there. If there is no base config use the platform default.
+        NetworkSecurityConfig.Builder platformDefaultBuilder =
+                NetworkSecurityConfig.getDefaultBuilder();
+        addDebugAnchorsIfNeeded(debugConfigBuilder, platformDefaultBuilder);
+        if (baseConfigBuilder != null) {
+            baseConfigBuilder.setParent(platformDefaultBuilder);
+            addDebugAnchorsIfNeeded(debugConfigBuilder, baseConfigBuilder);
+        } else {
+            baseConfigBuilder = platformDefaultBuilder;
+        }
+        // Build the per-domain config mapping.
+        Set<Pair<Domain, NetworkSecurityConfig>> configs = new ArraySet<>();
+
+        for (Pair<NetworkSecurityConfig.Builder, Set<Domain>> entry : builders) {
+            NetworkSecurityConfig.Builder builder = entry.first;
+            Set<Domain> domains = entry.second;
+            // Set the parent of configs that do not have a parent to the base-config. This can
+            // happen if the base-config comes after a domain-config in the file.
+            // Note that this is safe with regards to children because of the order that
+            // parseConfigEntry returns builders, the parent is always before the children. The
+            // children builders will not have build called until _after_ their parents have their
+            // parent set so everything is consistent.
+            if (builder.getParent() == null) {
+                builder.setParent(baseConfigBuilder);
+            }
+            addDebugAnchorsIfNeeded(debugConfigBuilder, builder);
+            NetworkSecurityConfig config = builder.build();
+            for (Domain domain : domains) {
+                configs.add(new Pair<>(domain, config));
+            }
+        }
+        mDefaultConfig = baseConfigBuilder.build();
+        mDomainMap = configs;
+    }
+
+    public static class ParserException extends Exception {
+
+        public ParserException(XmlPullParser parser, String message, Throwable cause) {
+            super(message + " at: " + parser.getPositionDescription(), cause);
+        }
+
+        public ParserException(XmlPullParser parser, String message) {
+            this(parser, message, null);
+        }
+    }
+}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 42402eb..1d1edaa 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1591,7 +1591,7 @@
                 if (mPendingConfiguration.seq != 0) {
                     if (DEBUG_CONFIGURATION) Log.v(TAG, "Visible with new config: "
                             + mPendingConfiguration);
-                    updateConfiguration(mPendingConfiguration, !mFirst);
+                    updateConfiguration(new Configuration(mPendingConfiguration), !mFirst);
                     mPendingConfiguration.seq = 0;
                 }
 
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 77f1efa..4bcfa4c 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -519,7 +519,7 @@
         String args[] = {
             "--setuid=1000",
             "--setgid=1000",
-            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007",
+            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1032,3001,3002,3003,3006,3007,3009",
             "--capabilities=" + capabilities + "," + capabilities,
             "--nice-name=system_server",
             "--runtime-args",
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 47b2f3b..4c68e01 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -273,7 +273,7 @@
 
 # -Wno-c++11-extensions: Clang warns about Skia using the C++11 override keyword, but this project
 #                        is not being compiled with that level. Remove once this has changed.
-LOCAL_CFLAGS += -Wno-c++11-extensions
+LOCAL_CLANG_CFLAGS += -Wno-c++11-extensions
 
 # b/22414716: thread_local (android/graphics/Paint.cpp) and Clang don't like each other at the
 #             moment.
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index cdce77c..734c50e 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -608,15 +608,6 @@
       kEMIntFast,
       kEMJitCompiler,
     } executionMode = kEMDefault;
-    char profilePeriod[sizeof("-Xprofile-period:")-1 + PROPERTY_VALUE_MAX];
-    char profileDuration[sizeof("-Xprofile-duration:")-1 + PROPERTY_VALUE_MAX];
-    char profileInterval[sizeof("-Xprofile-interval:")-1 + PROPERTY_VALUE_MAX];
-    char profileBackoff[sizeof("-Xprofile-backoff:")-1 + PROPERTY_VALUE_MAX];
-    char profileTopKThreshold[sizeof("-Xprofile-top-k-threshold:")-1 + PROPERTY_VALUE_MAX];
-    char profileTopKChangeThreshold[sizeof("-Xprofile-top-k-change-threshold:")-1 +
-                                    PROPERTY_VALUE_MAX];
-    char profileType[sizeof("-Xprofile-type:")-1 + PROPERTY_VALUE_MAX];
-    char profileMaxStackDepth[sizeof("-Xprofile-max-stack-depth:")-1 + PROPERTY_VALUE_MAX];
     char localeOption[sizeof("-Duser.locale=") + PROPERTY_VALUE_MAX];
     char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:")-1 + PROPERTY_VALUE_MAX];
     char nativeBridgeLibrary[sizeof("-XX:NativeBridge=") + PROPERTY_VALUE_MAX];
@@ -835,75 +826,22 @@
         addOption(localeOption);
     }
 
-    /*
-     * Set profiler options
-     */
-    // Whether or not the profiler should be enabled.
-    property_get("dalvik.vm.profiler", propBuf, "0");
-    if (propBuf[0] == '1') {
-        addOption("-Xenable-profiler");
-    }
-
-    // Whether the profile should start upon app startup or be delayed by some random offset
-    // (in seconds) that is bound between 0 and a fixed value.
-    property_get("dalvik.vm.profile.start-immed", propBuf, "0");
-    if (propBuf[0] == '1') {
-        addOption("-Xprofile-start-immediately");
-    }
-
-    // Number of seconds during profile runs.
-    parseRuntimeOption("dalvik.vm.profile.period-secs", profilePeriod, "-Xprofile-period:");
-
-    // Length of each profile run (seconds).
-    parseRuntimeOption("dalvik.vm.profile.duration-secs",
-                       profileDuration,
-                       "-Xprofile-duration:");
-
-    // Polling interval during profile run (microseconds).
-    parseRuntimeOption("dalvik.vm.profile.interval-us", profileInterval, "-Xprofile-interval:");
-
-    // Coefficient for period backoff.  The the period is multiplied
-    // by this value after each profile run.
-    parseRuntimeOption("dalvik.vm.profile.backoff-coeff", profileBackoff, "-Xprofile-backoff:");
-
-    // Top K% of samples that are considered relevant when
-    // deciding if the app should be recompiled.
-    parseRuntimeOption("dalvik.vm.profile.top-k-thr",
-                       profileTopKThreshold,
-                       "-Xprofile-top-k-threshold:");
-
-    // The threshold after which a change in the structure of the
-    // top K% profiled samples becomes significant and triggers
-    // recompilation. A change in profile is considered
-    // significant if X% (top-k-change-threshold) of the top K%
-    // (top-k-threshold property) samples has changed.
-    parseRuntimeOption("dalvik.vm.profile.top-k-ch-thr",
-                       profileTopKChangeThreshold,
-                       "-Xprofile-top-k-change-threshold:");
-
-    // Type of profile data.
-    parseRuntimeOption("dalvik.vm.profiler.type", profileType, "-Xprofile-type:");
-
-    // Depth of bounded stack data
-    parseRuntimeOption("dalvik.vm.profile.stack-depth",
-                       profileMaxStackDepth,
-                       "-Xprofile-max-stack-depth:");
-
-    /*
-     * Tracing options.
-     */
-    property_get("dalvik.vm.method-trace", propBuf, "false");
-    if (strcmp(propBuf, "true") == 0) {
-        addOption("-Xmethod-trace");
-        parseRuntimeOption("dalvik.vm.method-trace-file",
-                           methodTraceFileBuf,
-                           "-Xmethod-trace-file:");
-        parseRuntimeOption("dalvik.vm.method-trace-file-siz",
-                           methodTraceFileSizeBuf,
-                           "-Xmethod-trace-file-size:");
-        property_get("dalvik.vm.method-trace-stream", propBuf, "false");
+    // Trace files are stored in /data/misc/trace which is writable only in debug mode.
+    property_get("ro.debuggable", propBuf, "0");
+    if (strcmp(propBuf, "1") == 0) {
+        property_get("dalvik.vm.method-trace", propBuf, "false");
         if (strcmp(propBuf, "true") == 0) {
-            addOption("-Xmethod-trace-stream");
+            addOption("-Xmethod-trace");
+            parseRuntimeOption("dalvik.vm.method-trace-file",
+                               methodTraceFileBuf,
+                               "-Xmethod-trace-file:");
+            parseRuntimeOption("dalvik.vm.method-trace-file-siz",
+                               methodTraceFileSizeBuf,
+                               "-Xmethod-trace-file-size:");
+            property_get("dalvik.vm.method-trace-stream", propBuf, "false");
+            if (strcmp(propBuf, "true") == 0) {
+                addOption("-Xmethod-trace-stream");
+            }
         }
     }
 
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index ba39ba7..85cb4df 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -97,20 +97,21 @@
      */
     public static void install() {
         Provider[] providers = Security.getProviders();
-        int bcProviderPosition = -1;
-        for (int position = 0; position < providers.length; position++) {
-            Provider provider = providers[position];
+        int bcProviderIndex = -1;
+        for (int i = 0; i < providers.length; i++) {
+            Provider provider = providers[i];
             if ("BC".equals(provider.getName())) {
-                bcProviderPosition = position;
+                bcProviderIndex = i;
                 break;
             }
         }
 
         Security.addProvider(new AndroidKeyStoreProvider());
         Provider workaroundProvider = new AndroidKeyStoreBCWorkaroundProvider();
-        if (bcProviderPosition != -1) {
+        if (bcProviderIndex != -1) {
             // Bouncy Castle provider found -- install the workaround provider above it.
-            Security.insertProviderAt(workaroundProvider, bcProviderPosition);
+            // insertProviderAt uses 1-based positions.
+            Security.insertProviderAt(workaroundProvider, bcProviderIndex + 1);
         } else {
             // Bouncy Castle provider not found -- install the workaround provider at lowest
             // priority.
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 2292ef4..9621b54 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2325,12 +2325,15 @@
 
     PathTexture* texture = mCaches.pathCache.get(path, paint);
     if (!texture) return;
-    const AutoTexture autoCleanup(texture);
 
     const float x = texture->left - texture->offset;
     const float y = texture->top - texture->offset;
 
     drawPathTexture(texture, x, y, paint);
+
+    if (texture->cleanup) {
+        mCaches.pathCache.remove(path, paint);
+    }
     mDirty = true;
 }
 
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 3af640f..3236f6f 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -400,6 +400,13 @@
     return texture;
 }
 
+void PathCache::remove(const SkPath* path, const SkPaint* paint)
+{
+    PathDescription entry(kShapePath, paint);
+    entry.shape.path.mGenerationID = path->getGenerationID();
+    mCache.remove(entry);
+}
+
 void PathCache::precache(const SkPath* path, const SkPaint* paint) {
     if (!Caches::getInstance().tasks.canRunTasks()) {
         return;
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 7014863..c529915 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -201,6 +201,7 @@
     PathTexture* getArc(float width, float height, float startAngle, float sweepAngle,
             bool useCenter, const SkPaint* paint);
     PathTexture* get(const SkPath* path, const SkPaint* paint);
+    void         remove(const SkPath* path, const SkPaint* paint);
 
     /**
      * Removes the specified path. This is meant to be called from threads
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 09d1258..595c10c 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -80,6 +80,11 @@
     ALOGD("light center %f %f %f",
             adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z);
 #endif
+    if (isnan(adjustedLightCenter.x)
+            || isnan(adjustedLightCenter.y)
+            || isnan(adjustedLightCenter.z)) {
+        return;
+    }
 
     // light position (because it's in local space) needs to compensate for receiver transform
     // TODO: should apply to light orientation, not just position
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index b8c9804..3186a8e 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -742,7 +742,7 @@
             // vertex's location.
             int newPenumbraNumber = indexDelta - 1;
 
-            float accumulatedDeltaLength[newPenumbraNumber];
+            float accumulatedDeltaLength[indexDelta];
             float totalDeltaLength = 0;
 
             // To save time, cache the previous umbra vertex info outside the loop
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 6bf5721..445ee6f 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -17,6 +17,8 @@
 package android.media;
 
 import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import java.text.ParsePosition;
 import java.text.SimpleDateFormat;
 import java.util.Date;
@@ -128,6 +130,9 @@
     // use sLock to serialize the accesses.
     private static final Object sLock = new Object();
 
+    // Pattern to check non zero timestamp
+    private static final Pattern sNonZeroTimePattern = Pattern.compile(".*[1-9].*");
+
     /**
      * Reads Exif tags from the specified JPEG file.
      */
@@ -367,7 +372,8 @@
      */
     public long getDateTime() {
         String dateTimeString = mAttributes.get(TAG_DATETIME);
-        if (dateTimeString == null) return -1;
+        if (dateTimeString == null
+                || !sNonZeroTimePattern.matcher(dateTimeString).matches()) return -1;
 
         ParsePosition pos = new ParsePosition(0);
         try {
@@ -402,7 +408,9 @@
     public long getGpsDateTime() {
         String date = mAttributes.get(TAG_GPS_DATESTAMP);
         String time = mAttributes.get(TAG_GPS_TIMESTAMP);
-        if (date == null || time == null) return -1;
+        if (date == null || time == null
+                || (!sNonZeroTimePattern.matcher(date).matches()
+                && !sNonZeroTimePattern.matcher(time).matches())) return -1;
 
         String dateTimeString = date + ' ' + time;
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index e8a5e43..14bac4f 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -655,9 +655,11 @@
             }
         }
 
-        PrinterId printerId = mCurrentPrinter.getId();
-        final int index = mDestinationSpinnerAdapter.getPrinterIndex(printerId);
-        mDestinationSpinner.setSelection(index);
+        if (mCurrentPrinter != null) {
+            PrinterId printerId = mCurrentPrinter.getId();
+            final int index = mDestinationSpinnerAdapter.getPrinterIndex(printerId);
+            mDestinationSpinner.setSelection(index);
+        }
     }
 
     private void startAdvancedPrintOptionsActivity(PrinterInfo printer) {
diff --git a/rs/java/android/renderscript/ScriptGroup.java b/rs/java/android/renderscript/ScriptGroup.java
index 54180f4..9bbacbc 100644
--- a/rs/java/android/renderscript/ScriptGroup.java
+++ b/rs/java/android/renderscript/ScriptGroup.java
@@ -278,6 +278,8 @@
             public ValueAndSize(RenderScript rs, Object obj) {
                 if (obj instanceof Allocation) {
                     value = ((Allocation)obj).getID(rs);
+                    // Special value for size to tell the runtime and driver that
+                    // the value is an Allocation
                     size = -1;
                 } else if (obj instanceof Boolean) {
                     value = ((Boolean)obj).booleanValue() ? 1 : 0;
@@ -289,10 +291,10 @@
                     value = ((Long)obj).longValue();
                     size = 8;
                 } else if (obj instanceof Float) {
-                    value = ((Float)obj).longValue();
+                    value = Float.floatToRawIntBits(((Float)obj).floatValue());
                     size = 4;
                 } else if (obj instanceof Double) {
-                    value = ((Double)obj).longValue();
+                    value = Double.doubleToRawLongBits(((Double)obj).doubleValue());
                     size = 8;
                 }
             }
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index be7071e..113241d 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -393,7 +393,6 @@
 
   size_t numValues, numDependencies;
   RsScriptFieldID* fieldIDs;
-  uintptr_t* values;
   RsClosure* depClosures;
   RsScriptFieldID* depFieldIDs;
 
@@ -430,15 +429,6 @@
     fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
   }
 
-  values = (uintptr_t*)alloca(sizeof(uintptr_t) * numValues);
-  if (values == nullptr) {
-      goto exit;
-  }
-
-  for (size_t i = 0; i < numValues; i++) {
-    values[i] = (uintptr_t)jValues[i];
-  }
-
   depClosures = (RsClosure*)alloca(sizeof(RsClosure) * numDependencies);
   if (depClosures == nullptr) {
       goto exit;
@@ -459,7 +449,7 @@
 
   ret = (jlong)(uintptr_t)rsClosureCreate(
       (RsContext)con, (RsScriptKernelID)kernelID, (RsAllocation)returnValue,
-      fieldIDs, numValues, values, numValues,
+      fieldIDs, numValues, jValues, numValues,
       (int*)jSizes, numValues,
       depClosures, numDependencies,
       depFieldIDs, numDependencies);
@@ -511,7 +501,6 @@
 
   size_t numValues;
   RsScriptFieldID* fieldIDs;
-  uintptr_t* values;
 
   if (fieldIDs_length != values_length || values_length != sizes_length) {
       ALOGE("Unmatched field IDs, values, and sizes in closure creation.");
@@ -534,18 +523,9 @@
     fieldIDs[i] = (RsScriptFieldID)jFieldIDs[i];
   }
 
-  values = (uintptr_t*)alloca(sizeof(uintptr_t) * numValues);
-  if (values == nullptr) {
-      goto exit;
-  }
-
-  for (size_t i = 0; i < numValues; i++) {
-    values[i] = (uintptr_t)jValues[i];
-  }
-
   ret = (jlong)(uintptr_t)rsInvokeClosureCreate(
       (RsContext)con, (RsScriptInvokeID)invokeID, jParams, jParamLength,
-      fieldIDs, numValues, values, numValues,
+      fieldIDs, numValues, jValues, numValues,
       (int*)jSizes, numValues);
 
 exit:
@@ -561,15 +541,17 @@
 static void
 nClosureSetArg(JNIEnv *_env, jobject _this, jlong con, jlong closureID,
                jint index, jlong value, jint size) {
+  // Size is signed with -1 indicating the value is an Allocation
   rsClosureSetArg((RsContext)con, (RsClosure)closureID, (uint32_t)index,
-                  (uintptr_t)value, (size_t)size);
+                  (uintptr_t)value, size);
 }
 
 static void
 nClosureSetGlobal(JNIEnv *_env, jobject _this, jlong con, jlong closureID,
                   jlong fieldID, jlong value, jint size) {
+  // Size is signed with -1 indicating the value is an Allocation
   rsClosureSetGlobal((RsContext)con, (RsClosure)closureID,
-                     (RsScriptFieldID)fieldID, (uintptr_t)value, (size_t)size);
+                     (RsScriptFieldID)fieldID, (int64_t)value, size);
 }
 
 static long
diff --git a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
index b4613d6..a8e27a8 100644
--- a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
+++ b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
@@ -372,6 +372,9 @@
 
     @Override
     public void onDestroy() {
+        if (mMagnificationController != null) {
+            mMagnificationController.cancelAnimation();
+        }
         mScreenStateObserver.destroy();
         mWindowManager.setMagnificationCallbacks(null);
     }
@@ -988,10 +991,14 @@
             return mCurrentMagnificationSpec.scale > 1.0f;
         }
 
-        public void reset(boolean animate) {
+        public void cancelAnimation() {
             if (mTransformationAnimator.isRunning()) {
                 mTransformationAnimator.cancel();
             }
+        }
+
+        public void reset(boolean animate) {
+            cancelAnimation();
             mCurrentMagnificationSpec.clear();
             if (animate) {
                 animateMangificationSpec(mSentMagnificationSpec,
@@ -1056,9 +1063,7 @@
                             centerY) == 0) {
                 return;
             }
-            if (mTransformationAnimator.isRunning()) {
-                mTransformationAnimator.cancel();
-            }
+            cancelAnimation();
             if (DEBUG_MAGNIFICATION_CONTROLLER) {
                 Slog.i(LOG_TAG, "scale: " + scale + " offsetX: " + centerX
                         + " offsetY: " + centerY);
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index c228422..9eb66dd 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -88,6 +88,7 @@
     private SettingsObserver mSettingObserver;
 
     native static boolean vibratorExists();
+    native static void vibratorInit();
     native static void vibratorOn(long milliseconds);
     native static void vibratorOff();
 
@@ -195,6 +196,7 @@
     }
 
     VibratorService(Context context) {
+        vibratorInit();
         // Reset the hardware to a default state, in case this is a runtime
         // restart instead of a fresh boot.
         vibratorOff();
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index 6fbe8e4..a5c6180 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -20,6 +20,7 @@
 import android.app.DownloadManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal.PackagesProvider;
@@ -673,7 +674,12 @@
             Intent intent, int userId) {
         ResolveInfo handler = mService.resolveIntent(intent,
                 intent.resolveType(mService.mContext.getContentResolver()), 0, userId);
-        if (handler == null) {
+        if (handler == null || handler.activityInfo == null) {
+            return null;
+        }
+        ActivityInfo activityInfo = handler.activityInfo;
+        if (activityInfo.packageName.equals(mService.mResolveActivity.packageName)
+                && activityInfo.name.equals(mService.mResolveActivity.name)) {
             return null;
         }
         return getSystemPackageLPr(handler.activityInfo.packageName);
@@ -842,7 +848,7 @@
             return false;
         }
         PackageSetting sysPkg = mService.mSettings.getDisabledSystemPkgLPr(pkg.packageName);
-        if (sysPkg != null) {
+        if (sysPkg != null && sysPkg.pkg != null) {
             if ((sysPkg.pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
                 return false;
             }
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index 64278ed..03fbd19 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -22,32 +22,69 @@
 
 #include <utils/misc.h>
 #include <utils/Log.h>
-#include <hardware_legacy/vibrator.h>
+#include <hardware/vibrator.h>
 
 #include <stdio.h>
 
 namespace android
 {
 
+static hw_module_t *gVibraModule = NULL;
+static vibrator_device_t *gVibraDevice = NULL;
+
+static void vibratorInit(JNIEnv /* env */, jobject /* clazz */)
+{
+    if (gVibraModule != NULL) {
+        return;
+    }
+
+    int err = hw_get_module(VIBRATOR_HARDWARE_MODULE_ID, (hw_module_t const**)&gVibraModule);
+
+    if (err) {
+        ALOGE("Couldn't load %s module (%s)", VIBRATOR_HARDWARE_MODULE_ID, strerror(-err));
+    } else {
+        if (gVibraModule) {
+            vibrator_open(gVibraModule, &gVibraDevice);
+        }
+    }
+}
+
 static jboolean vibratorExists(JNIEnv* /* env */, jobject /* clazz */)
 {
-    return vibrator_exists() > 0 ? JNI_TRUE : JNI_FALSE;
+    if (gVibraModule && gVibraDevice) {
+        return JNI_TRUE;
+    } else {
+        return JNI_FALSE;
+    }
 }
 
 static void vibratorOn(JNIEnv* /* env */, jobject /* clazz */, jlong timeout_ms)
 {
-    // ALOGI("vibratorOn\n");
-    vibrator_on(timeout_ms);
+    if (gVibraDevice) {
+        int err = gVibraDevice->vibrator_on(gVibraDevice, timeout_ms);
+        if (err != 0) {
+            ALOGE("The hw module failed in vibrator_on: %s", strerror(-err));
+        }
+    } else {
+        ALOGW("Tried to vibrate but there is no vibrator device.");
+    }
 }
 
 static void vibratorOff(JNIEnv* /* env */, jobject /* clazz */)
 {
-    // ALOGI("vibratorOff\n");
-    vibrator_off();
+    if (gVibraDevice) {
+        int err = gVibraDevice->vibrator_off(gVibraDevice);
+        if (err != 0) {
+            ALOGE("The hw module failed in vibrator_off(): %s", strerror(-err));
+        }
+    } else {
+        ALOGW("Tried to stop vibrating but there is no vibrator device.");
+    }
 }
 
 static const JNINativeMethod method_table[] = {
     { "vibratorExists", "()Z", (void*)vibratorExists },
+    { "vibratorInit", "()V", (void*)vibratorInit },
     { "vibratorOn", "(J)V", (void*)vibratorOn },
     { "vibratorOff", "()V", (void*)vibratorOff }
 };
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 4146c1c0..c518f77a 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -218,7 +218,7 @@
             synchronized (this) {
                 mScreenOnTime = readScreenOnTimeLocked();
             }
-            mDisplayManager.registerDisplayListener(mDisplayListener, null);
+            mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
             synchronized (this) {
                 updateDisplayLocked();
             }
diff --git a/tests/NetworkSecurityConfigTest/Android.mk b/tests/NetworkSecurityConfigTest/Android.mk
new file mode 100644
index 0000000..a63162d
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/Android.mk
@@ -0,0 +1,15 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE := platform
+
+LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle conscrypt
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := NetworkSecurityConfigTests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/NetworkSecurityConfigTest/AndroidManifest.xml b/tests/NetworkSecurityConfigTest/AndroidManifest.xml
new file mode 100644
index 0000000..4c1fbd3
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.security.net.config"
+          android:sharedUserId="android.uid.system">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="android.security.net.config"
+        android:label="ANSC Tests">
+    </instrumentation>
+</manifest>
diff --git a/tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der
new file mode 100644
index 0000000..235bd47
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_der.der
Binary files differ
diff --git a/tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem
new file mode 100644
index 0000000..413e3c0
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/raw/ca_certs_pem.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIDfTCCAuagAwIBAgIDErvmMA0GCSqGSIb3DQEBBQUAME4xCzAJBgNVBAYTAlVT
+MRAwDgYDVQQKEwdFcXVpZmF4MS0wKwYDVQQLEyRFcXVpZmF4IFNlY3VyZSBDZXJ0
+aWZpY2F0ZSBBdXRob3JpdHkwHhcNMDIwNTIxMDQwMDAwWhcNMTgwODIxMDQwMDAw
+WjBCMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UE
+AxMSR2VvVHJ1c3QgR2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
+CgKCAQEA2swYYzD99BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9m
+OSm9BXiLnTjoBbdqfnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIu
+T8rxh0PBFpVXLVDviS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6c
+JmTM386DGXHKTubU1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmR
+Cw7+OC7RHQWa9k0+bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5asz
+PeE4uwc2hGKceeoWMPRfwCvocWvk+QIDAQABo4HwMIHtMB8GA1UdIwQYMBaAFEjm
+aPkr0rKV10fYIyAQTzOYkJ/UMB0GA1UdDgQWBBTAephojYn7qwVkDBF9qn1luMrM
+TjAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjA6BgNVHR8EMzAxMC+g
+LaArhilodHRwOi8vY3JsLmdlb3RydXN0LmNvbS9jcmxzL3NlY3VyZWNhLmNybDBO
+BgNVHSAERzBFMEMGBFUdIAAwOzA5BggrBgEFBQcCARYtaHR0cHM6Ly93d3cuZ2Vv
+dHJ1c3QuY29tL3Jlc291cmNlcy9yZXBvc2l0b3J5MA0GCSqGSIb3DQEBBQUAA4GB
+AHbhEm5OSxYShjAGsoEIz/AIx8dxfmbuwu3UOx//8PDITtZDOLC5MH0Y0FWDomrL
+NhGc6Ehmo21/uBPUR/6LWlxz/K7ZGzIZOKuXNBSqltLroxwUCEm2u+WR74M26x1W
+b8ravHNjkOR/ez4iyz0H7V84dJzjA1BOoa+Y7mHyhD8S
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICPDCCAaUCEDyRMcsf9tAbDpq40ES/Er4wDQYJKoZIhvcNAQEFBQAwXzELMAkG
+A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
+cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
+MDEyOTAwMDAwMFoXDTI4MDgwMjIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
+BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
+YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
+ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
+BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
+I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
+CSqGSIb3DQEBBQUAA4GBABByUqkFFBkyCEHwxWsKzH4PIRnN5GfcX6kb5sroc50i
+2JhucwNhkcV8sEVAbkSdjbCxlnRhLQ2pRdKkkirWmnWXbj9T/UWZYB2oK0z5XqcJ
+2HUw19JlYD1n1khVdWk/kfVIC0dpImmClr7JyDiGSnoscxlIaU5rfGW/D/xwzoiQ
+-----END CERTIFICATE-----
diff --git a/tests/NetworkSecurityConfigTest/res/raw/test_debug_ca.pem b/tests/NetworkSecurityConfigTest/res/raw/test_debug_ca.pem
new file mode 100644
index 0000000..81648d9
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/raw/test_debug_ca.pem
@@ -0,0 +1,19 @@
+-----BEGIN CERTIFICATE-----
+MIIDITCCAgmgAwIBAgIJAP/YiWztz/J7MA0GCSqGSIb3DQEBCwUAMCcxFjAUBgNV
+BAMMDVRlc3QgZGVidWcgQ0ExDTALBgNVBAoMBEFPU1AwHhcNMTUxMTA5MjEyNjQ2
+WhcNMTgwODI5MjEyNjQ2WjAnMRYwFAYDVQQDDA1UZXN0IGRlYnVnIENBMQ0wCwYD
+VQQKDARBT1NQMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuPFmkOJj
+ehjfvdDr2qTcBWNqNATrW1SuM88Vj00ubUFQ4tZElozj8YnQOw1FeC79c1k88b8R
+6jcqYYp/mw2JYoD6yWcFPHo5BplIpk0EhIUARH/aeoclHvsUN2GGDyTO0vf0CfJn
+9Wp6lSLjyq7V/6tYdk+0cL632t56MHp8TCO+AaveYP1T8JZqx0/50xNcsK7lIqNa
+ctWyRGFxR4ifdVsgkw9WhAB/Ow2uOwN9uLGqzsCd+yXW2weX52EIivoTGZfJo+U8
+Fi0ygnCHBv2jsJA7yWLhHmZ4ijsVtfutIKmN0w+DHkl6S25girXhy0zJp/1QvHGm
+jaF60V1gw471jQIDAQABo1AwTjAdBgNVHQ4EFgQUoq66jncy83L5eeyW1g78s/uq
+iyQwHwYDVR0jBBgwFoAUoq66jncy83L5eeyW1g78s/uqiyQwDAYDVR0TBAUwAwEB
+/zANBgkqhkiG9w0BAQsFAAOCAQEAohytuH4CdX0gO8EGVDRVurRH7LO69lwd/6Iw
+hJ1lIK/mzj5RM2itVGTkintyHCLu5giVkHn4FHg4X9qzZaTPOcXv9ntQNS2nacZe
+bY8nfhsAhstJT4nIOWHE3FrZkMDOK6nZHIzfscX3V/VVq5MeA+WzXwmKp6MBNr+E
+oUegXCGjd26Bl6SFz3rD7Qh+dzSTtyf/ECzXaMjpZu3k6fb4EgRz6vdBCHKKtpv6
+Mxcr0nLwdI6LnAGXvJLV4sj+l6Ngg00EeyorG8ATgtmsUrXXOR1e+yDCQv6fjQfs
+CWYztECAUE9hfCXJwb0TBrq9YeJAvcO7iE6S0Pq+X3xNtetE1A==
+-----END CERTIFICATE-----
diff --git a/tests/NetworkSecurityConfigTest/res/xml/attributes.xml b/tests/NetworkSecurityConfigTest/res/xml/attributes.xml
new file mode 100644
index 0000000..eff13c8
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/attributes.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <base-config cleartextTrafficPermitted="false" hstsEnforced="true">
+  </base-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/bad_config0.xml b/tests/NetworkSecurityConfigTest/res/xml/bad_config0.xml
new file mode 100644
index 0000000..6af855d
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/bad_config0.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <domain-config>
+    <domain>android.com</domain>
+    <pin-set>
+      <!-- Bad pin digest -->
+      <pin digest="I am probably not an algorithm">1HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
+    </pin-set>
+  </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/bad_config1.xml b/tests/NetworkSecurityConfigTest/res/xml/bad_config1.xml
new file mode 100644
index 0000000..d683b74a
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/bad_config1.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <domain-config>
+    <domain>android.com</domain>
+    <pin-set>
+    <!-- Unknown pin digest -->
+      <pin>1HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
+    </pin-set>
+  </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/bad_config2.xml b/tests/NetworkSecurityConfigTest/res/xml/bad_config2.xml
new file mode 100644
index 0000000..6f3f8b4
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/bad_config2.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <domain-config>
+    <domain>android.com</domain>
+    <pin-set>
+      <!-- empty digest -->
+      <pin digest="SHA-256"></pin>
+    </pin-set>
+  </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/bad_config3.xml b/tests/NetworkSecurityConfigTest/res/xml/bad_config3.xml
new file mode 100644
index 0000000..fb2126c
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/bad_config3.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <domain-config>
+    <domain>android.com</domain>
+  </domain-config>
+  <domain-config>
+    <!-- Same domain name used in two configs -->
+    <domain>android.com</domain>
+  </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/bad_config4.xml b/tests/NetworkSecurityConfigTest/res/xml/bad_config4.xml
new file mode 100644
index 0000000..95972ce
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/bad_config4.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <base-config>
+    <!-- domains are not allowed in base-config -->
+    <domain>android.com</domain>
+  </base-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/bad_config5.xml b/tests/NetworkSecurityConfigTest/res/xml/bad_config5.xml
new file mode 100644
index 0000000..8b6b721
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/bad_config5.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <base-config>
+    <!-- pins are not allowed in base-config -->
+    <pin-set>
+    </pin-set>
+  </base-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/bad_pin.xml b/tests/NetworkSecurityConfigTest/res/xml/bad_pin.xml
new file mode 100644
index 0000000..62a7b88
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/bad_pin.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <domain-config>
+    <domain>android.com</domain>
+    <pin-set>
+      <pin digest="SHA-256">1HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
+    </pin-set>
+  </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/debug_basic.xml b/tests/NetworkSecurityConfigTest/res/xml/debug_basic.xml
new file mode 100644
index 0000000..8da9317
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/debug_basic.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <base-config>
+    <trust-anchors>
+    </trust-anchors>
+  </base-config>
+  <debug-overrides>
+    <trust-anchors>
+      <certificates src="system" />
+    </trust-anchors>
+  </debug-overrides>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/debug_domain.xml b/tests/NetworkSecurityConfigTest/res/xml/debug_domain.xml
new file mode 100644
index 0000000..24eed7a
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/debug_domain.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <domain-config>
+    <domain>android.com</domain>
+    <trust-anchors>
+      <certificates src="@raw/ca_certs_pem" />
+    </trust-anchors>
+  </domain-config>
+  <debug-overrides>
+    <trust-anchors>
+      <certificates src="@raw/test_debug_ca" />
+    </trust-anchors>
+  </debug-overrides>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/debug_inherit.xml b/tests/NetworkSecurityConfigTest/res/xml/debug_inherit.xml
new file mode 100644
index 0000000..ce0cbc8
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/debug_inherit.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <debug-overrides>
+    <trust-anchors>
+      <certificates src="@raw/test_debug_ca" />
+    </trust-anchors>
+  </debug-overrides>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/domain1.xml b/tests/NetworkSecurityConfigTest/res/xml/domain1.xml
new file mode 100644
index 0000000..6d8565c
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/domain1.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <base-config>
+    <trust-anchors>
+    </trust-anchors>
+  </base-config>
+  <domain-config>
+    <domain>android.com</domain>
+    <trust-anchors>
+      <certificates src="system" />
+      <certificates src="user" />
+    </trust-anchors>
+  </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/empty_config.xml b/tests/NetworkSecurityConfigTest/res/xml/empty_config.xml
new file mode 100644
index 0000000..1bd94b6
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/empty_config.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/empty_trust.xml b/tests/NetworkSecurityConfigTest/res/xml/empty_trust.xml
new file mode 100644
index 0000000..8093b9d
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/empty_trust.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <base-config>
+    <trust-anchors>
+    </trust-anchors>
+  </base-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/expired_pin.xml b/tests/NetworkSecurityConfigTest/res/xml/expired_pin.xml
new file mode 100644
index 0000000..f9f8465
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/expired_pin.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <domain-config>
+    <domain>android.com</domain>
+    <!-- Invalid pin that has expired -->
+    <pin-set expiration="2015-01-01">
+      <pin digest="SHA-256">aaaaaaaaaaa2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
+    </pin-set>
+  </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/multiple_configs.xml b/tests/NetworkSecurityConfigTest/res/xml/multiple_configs.xml
new file mode 100644
index 0000000..df08467
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/multiple_configs.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <base-config>
+    <trust-anchors>
+    </trust-anchors>
+  </base-config>
+  <domain-config>
+    <domain>android.com</domain>
+    <trust-anchors>
+      <certificates src="system" />
+      <certificates src="user" />
+    </trust-anchors>
+  </domain-config>
+  <domain-config>
+    <domain>google.com</domain>
+    <trust-anchors>
+      <certificates src="system" />
+      <certificates src="user" />
+    </trust-anchors>
+  </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/multiple_domains.xml b/tests/NetworkSecurityConfigTest/res/xml/multiple_domains.xml
new file mode 100644
index 0000000..9743c5f
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/multiple_domains.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <base-config>
+    <trust-anchors>
+    </trust-anchors>
+  </base-config>
+  <domain-config>
+    <domain>android.com</domain>
+    <domain>google.com</domain>
+    <trust-anchors>
+      <certificates src="system" />
+      <certificates src="user" />
+    </trust-anchors>
+  </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml b/tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml
new file mode 100644
index 0000000..d45fd77
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/nested_domains.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+    <domain-config>
+      <domain includeSubdomains="true">android.com</domain>
+      <trust-anchors>
+          <certificates src="system" />
+      </trust-anchors>
+      <!-- nested config that adds pins -->
+      <domain-config>
+          <domain>developer.android.com</domain>
+          <pin-set>
+              <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
+          </pin-set>
+      </domain-config>
+    </domain-config>
+    <base-config cleartextTrafficPermitted="false">
+    </base-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/nested_domains_override.xml b/tests/NetworkSecurityConfigTest/res/xml/nested_domains_override.xml
new file mode 100644
index 0000000..84e06e3
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/nested_domains_override.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+    <base-config cleartextTrafficPermitted="false">
+    </base-config>
+    <!-- Nested config that overrides parent -->
+    <domain-config cleartextTrafficPermitted="true">
+        <domain includeSubdomains="true">android.com</domain>
+        <domain-config cleartextTrafficPermitted="false">
+            <domain>developer.android.com</domain>
+        </domain-config>
+    </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/override_pins.xml b/tests/NetworkSecurityConfigTest/res/xml/override_pins.xml
new file mode 100644
index 0000000..785714a
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/override_pins.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <domain-config>
+    <domain>android.com</domain>
+    <pin-set>
+      <pin digest="SHA-256">aaaaaaaaIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
+    </pin-set>
+    <trust-anchors>
+      <certificates src="system" overridePins="true" />
+    </trust-anchors>
+  </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/pins1.xml b/tests/NetworkSecurityConfigTest/res/xml/pins1.xml
new file mode 100644
index 0000000..1773d280
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/pins1.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <domain-config>
+    <domain>android.com</domain>
+    <pin-set>
+      <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
+    </pin-set>
+  </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/resource_anchors_der.xml b/tests/NetworkSecurityConfigTest/res/xml/resource_anchors_der.xml
new file mode 100644
index 0000000..dfd6fd9
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/resource_anchors_der.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <base-config>
+    <trust-anchors>
+    </trust-anchors>
+  </base-config>
+  <domain-config>
+    <domain>android.com</domain>
+    <trust-anchors>
+      <certificates src="@raw/ca_certs_der" />
+    </trust-anchors>
+  </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/resource_anchors_pem.xml b/tests/NetworkSecurityConfigTest/res/xml/resource_anchors_pem.xml
new file mode 100644
index 0000000..894f29b
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/resource_anchors_pem.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <base-config>
+    <trust-anchors>
+    </trust-anchors>
+  </base-config>
+  <domain-config>
+    <domain>android.com</domain>
+    <trust-anchors>
+      <certificates src="@raw/ca_certs_pem" />
+    </trust-anchors>
+  </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/res/xml/subdomains.xml b/tests/NetworkSecurityConfigTest/res/xml/subdomains.xml
new file mode 100644
index 0000000..482b26c
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/res/xml/subdomains.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<network-security-config>
+  <base-config>
+    <trust-anchors>
+    </trust-anchors>
+  </base-config>
+  <domain-config>
+    <domain includeSubdomains="true">android.com</domain>
+    <trust-anchors>
+      <certificates src="system" />
+      <certificates src="user" />
+    </trust-anchors>
+  </domain-config>
+</network-security-config>
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java
new file mode 100644
index 0000000..9f48d56
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/NetworkSecurityConfigTests.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import android.app.Activity;
+import android.test.ActivityUnitTestCase;
+import android.util.ArraySet;
+import android.util.Pair;
+import java.io.IOException;
+import java.net.Socket;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.TrustManager;
+
+public class NetworkSecurityConfigTests extends ActivityUnitTestCase<Activity> {
+
+    public NetworkSecurityConfigTests() {
+        super(Activity.class);
+    }
+
+    // SHA-256 of the G2 intermediate CA for android.com (as of 10/2015).
+    private static final byte[] G2_SPKI_SHA256
+            = hexToBytes("ec722969cb64200ab6638f68ac538e40abab5b19a6485661042a1061c4612776");
+
+    private static byte[] hexToBytes(String s) {
+        int len = s.length();
+        byte[] data = new byte[len / 2];
+        for (int i = 0; i < len; i += 2) {
+            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(
+                    s.charAt(i + 1), 16));
+        }
+        return data;
+    }
+
+
+
+    /**
+     * Return a NetworkSecurityConfig that has an empty TrustAnchor set. This should always cause a
+     * SSLHandshakeException when used for a connection.
+     */
+    private NetworkSecurityConfig getEmptyConfig() {
+        return new NetworkSecurityConfig.Builder().build();
+    }
+
+    private NetworkSecurityConfig getSystemStoreConfig() {
+        return new NetworkSecurityConfig.Builder()
+                .addCertificatesEntryRef(
+                        new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+                .build();
+    }
+
+    public void testEmptyConfig() throws Exception {
+        ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+                = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+        ConfigSource testSource =
+                new TestConfigSource(domainMap, getEmptyConfig());
+        SSLContext context = TestUtils.getSSLContext(testSource);
+        TestUtils.assertConnectionFails(context, "android.com", 443);
+    }
+
+    public void testEmptyPerNetworkSecurityConfig() throws Exception {
+        ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+                = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+        domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+                new Domain("android.com", true), getEmptyConfig()));
+        NetworkSecurityConfig defaultConfig = getSystemStoreConfig();
+        SSLContext context = TestUtils.getSSLContext(new TestConfigSource(domainMap, defaultConfig));
+        TestUtils.assertConnectionFails(context, "android.com", 443);
+        TestUtils.assertConnectionSucceeds(context, "google.com", 443);
+    }
+
+    public void testBadPin() throws Exception {
+        ArraySet<Pin> pins = new ArraySet<Pin>();
+        pins.add(new Pin("SHA-256", new byte[0]));
+        NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
+                .setPinSet(new PinSet(pins, Long.MAX_VALUE))
+                .addCertificatesEntryRef(
+                        new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+                .build();
+        ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+                = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+        domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+                new Domain("android.com", true), domain));
+        SSLContext context
+                = TestUtils.getSSLContext(new TestConfigSource(domainMap, getSystemStoreConfig()));
+        TestUtils.assertConnectionFails(context, "android.com", 443);
+        TestUtils.assertConnectionSucceeds(context, "google.com", 443);
+    }
+
+    public void testGoodPin() throws Exception {
+        ArraySet<Pin> pins = new ArraySet<Pin>();
+        pins.add(new Pin("SHA-256", G2_SPKI_SHA256));
+        NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
+                .setPinSet(new PinSet(pins, Long.MAX_VALUE))
+                .addCertificatesEntryRef(
+                        new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+                .build();
+        ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+                = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+        domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+                new Domain("android.com", true), domain));
+        SSLContext context
+                = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
+        TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+        TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+    }
+
+    public void testOverridePins() throws Exception {
+        // Use a bad pin + granting the system CA store the ability to override pins.
+        ArraySet<Pin> pins = new ArraySet<Pin>();
+        pins.add(new Pin("SHA-256", new byte[0]));
+        NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
+                .setPinSet(new PinSet(pins, Long.MAX_VALUE))
+                .addCertificatesEntryRef(
+                        new CertificatesEntryRef(SystemCertificateSource.getInstance(), true))
+                .build();
+        ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+                = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+        domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+                new Domain("android.com", true), domain));
+        SSLContext context
+                = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
+        TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+    }
+
+    public void testMostSpecificNetworkSecurityConfig() throws Exception {
+        ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+                = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+        domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+                new Domain("android.com", true), getEmptyConfig()));
+        domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+                new Domain("developer.android.com", false), getSystemStoreConfig()));
+        SSLContext context
+                = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
+        TestUtils.assertConnectionFails(context, "android.com", 443);
+        TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+    }
+
+    public void testSubdomainIncluded() throws Exception {
+        // First try connecting to a subdomain of a domain entry that includes subdomains.
+        ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+                = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+        domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+                new Domain("android.com", true), getSystemStoreConfig()));
+        SSLContext context
+                = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
+        TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+        // Now try without including subdomains.
+        domainMap = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+        domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+                new Domain("android.com", false), getSystemStoreConfig()));
+        context = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
+        TestUtils.assertConnectionFails(context, "developer.android.com", 443);
+    }
+
+    public void testConfigBuilderUsesParents() throws Exception {
+        // Check that a builder with a parent uses the parent's values when non is set.
+        NetworkSecurityConfig config = new NetworkSecurityConfig.Builder()
+                .setParent(NetworkSecurityConfig.getDefaultBuilder())
+                .build();
+        assert(!config.getTrustAnchors().isEmpty());
+    }
+
+    public void testConfigBuilderParentLoop() throws Exception {
+        NetworkSecurityConfig.Builder config1 = new NetworkSecurityConfig.Builder();
+        NetworkSecurityConfig.Builder config2 = new NetworkSecurityConfig.Builder();
+        config1.setParent(config2);
+        try {
+            config2.setParent(config1);
+            fail("Loop in NetworkSecurityConfig parents");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    public void testWithUrlConnection() throws Exception {
+        ArraySet<Pin> pins = new ArraySet<Pin>();
+        pins.add(new Pin("SHA-256", G2_SPKI_SHA256));
+        NetworkSecurityConfig domain = new NetworkSecurityConfig.Builder()
+                .setPinSet(new PinSet(pins, Long.MAX_VALUE))
+                .addCertificatesEntryRef(
+                        new CertificatesEntryRef(SystemCertificateSource.getInstance(), false))
+                .build();
+        ArraySet<Pair<Domain, NetworkSecurityConfig>> domainMap
+                = new ArraySet<Pair<Domain, NetworkSecurityConfig>>();
+        domainMap.add(new Pair<Domain, NetworkSecurityConfig>(
+                new Domain("android.com", true), domain));
+        SSLContext context
+                = TestUtils.getSSLContext(new TestConfigSource(domainMap, getEmptyConfig()));
+        TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+        TestUtils.assertUrlConnectionSucceeds(context, "developer.android.com", 443);
+        TestUtils.assertUrlConnectionFails(context, "google.com", 443);
+    }
+}
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
new file mode 100644
index 0000000..92eadc0
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestCertificateSource.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import java.util.Set;
+import java.security.cert.X509Certificate;
+
+/** @hide */
+public class TestCertificateSource implements CertificateSource {
+
+    private final Set<X509Certificate> mCertificates;
+    public TestCertificateSource(Set<X509Certificate> certificates) {
+        mCertificates = certificates;
+    }
+
+    public Set<X509Certificate> getCertificates() {
+            return mCertificates;
+    }
+}
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestConfigSource.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestConfigSource.java
new file mode 100644
index 0000000..609f481
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestConfigSource.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import android.util.Pair;
+import java.util.Set;
+
+/** @hide */
+public class TestConfigSource implements ConfigSource {
+    private final Set<Pair<Domain, NetworkSecurityConfig>> mConfigs;
+    private final NetworkSecurityConfig mDefaultConfig;
+    public TestConfigSource(Set<Pair<Domain, NetworkSecurityConfig>> configs,
+            NetworkSecurityConfig defaultConfig) {
+        mConfigs = configs;
+        mDefaultConfig = defaultConfig;
+    }
+
+    public Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs() {
+        return mConfigs;
+    }
+
+    public NetworkSecurityConfig getDefaultConfig() {
+        return mDefaultConfig;
+    }
+}
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java
new file mode 100644
index 0000000..f7590fd
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/TestUtils.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import java.net.Socket;
+import java.net.URL;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+
+import junit.framework.Assert;
+
+public final class TestUtils extends Assert {
+
+    private TestUtils() {
+    }
+
+    public static void assertConnectionFails(SSLContext context, String host, int port)
+            throws Exception {
+        try {
+            Socket s = context.getSocketFactory().createSocket(host, port);
+            s.getInputStream();
+            fail("Expected connection to " + host + ":" + port + " to fail.");
+        } catch (SSLHandshakeException expected) {
+        }
+    }
+
+    public static void assertConnectionSucceeds(SSLContext context, String host, int port)
+            throws Exception {
+        Socket s = context.getSocketFactory().createSocket(host, port);
+        s.getInputStream();
+    }
+
+    public static void assertUrlConnectionFails(SSLContext context, String host, int port)
+            throws Exception {
+        URL url = new URL("https://" + host + ":" + port);
+        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
+        connection.setSSLSocketFactory(context.getSocketFactory());
+        try {
+            connection.getInputStream();
+            fail("Connection to " + host + ":" + port + " expected to fail");
+        } catch (SSLHandshakeException expected) {
+            // ignored.
+        }
+    }
+
+    public static void assertUrlConnectionSucceeds(SSLContext context, String host, int port)
+            throws Exception {
+        URL url = new URL("https://" + host + ":" + port);
+        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
+        connection.setSSLSocketFactory(context.getSocketFactory());
+        connection.getInputStream();
+    }
+
+    public static SSLContext getSSLContext(ConfigSource source) throws Exception {
+        ApplicationConfig config = new ApplicationConfig(source);
+        TrustManagerFactory tmf =
+                TrustManagerFactory.getInstance("PKIX", new NetworkSecurityConfigProvider());
+        tmf.init(new RootTrustManagerFactorySpi.ApplicationConfigParameters(config));
+        SSLContext context = SSLContext.getInstance("TLS");
+        context.init(null, tmf.getTrustManagers(), null);
+        return context;
+    }
+}
diff --git a/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
new file mode 100644
index 0000000..c6f3680
--- /dev/null
+++ b/tests/NetworkSecurityConfigTest/src/android/security/net/config/XmlConfigTests.java
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+import android.content.Context;
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+import android.util.ArraySet;
+import android.util.Pair;
+import java.io.IOException;
+import java.net.Socket;
+import java.net.URL;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.Security;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Set;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLHandshakeException;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+
+public class XmlConfigTests extends AndroidTestCase {
+
+    private final static String DEBUG_CA_SUBJ = "O=AOSP, CN=Test debug CA";
+
+    public void testEmptyConfigFile() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.empty_config);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        assertFalse(appConfig.hasPerDomainConfigs());
+        NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+        assertNotNull(config);
+        // Check defaults.
+        assertTrue(config.isCleartextTrafficPermitted());
+        assertFalse(config.isHstsEnforced());
+        assertFalse(config.getTrustAnchors().isEmpty());
+        PinSet pinSet = config.getPins();
+        assertTrue(pinSet.pins.isEmpty());
+        // Try some connections.
+        SSLContext context = TestUtils.getSSLContext(source);
+        TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+        TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+        TestUtils.assertUrlConnectionSucceeds(context, "google.com", 443);
+    }
+
+    public void testEmptyAnchors() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.empty_trust);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        assertFalse(appConfig.hasPerDomainConfigs());
+        NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+        assertNotNull(config);
+        // Check defaults.
+        assertTrue(config.isCleartextTrafficPermitted());
+        assertFalse(config.isHstsEnforced());
+        assertTrue(config.getTrustAnchors().isEmpty());
+        PinSet pinSet = config.getPins();
+        assertTrue(pinSet.pins.isEmpty());
+        SSLContext context = TestUtils.getSSLContext(source);
+        TestUtils.assertConnectionFails(context, "android.com", 443);
+        TestUtils.assertConnectionFails(context, "developer.android.com", 443);
+        TestUtils.assertUrlConnectionFails(context, "google.com", 443);
+    }
+
+    public void testBasicDomainConfig() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.domain1);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        assertTrue(appConfig.hasPerDomainConfigs());
+        NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+        assertNotNull(config);
+        // Check defaults.
+        assertTrue(config.isCleartextTrafficPermitted());
+        assertFalse(config.isHstsEnforced());
+        assertTrue(config.getTrustAnchors().isEmpty());
+        PinSet pinSet = config.getPins();
+        assertTrue(pinSet.pins.isEmpty());
+        // Check android.com.
+        config = appConfig.getConfigForHostname("android.com");
+        assertTrue(config.isCleartextTrafficPermitted());
+        assertFalse(config.isHstsEnforced());
+        assertFalse(config.getTrustAnchors().isEmpty());
+        pinSet = config.getPins();
+        assertTrue(pinSet.pins.isEmpty());
+        // Try connections.
+        SSLContext context = TestUtils.getSSLContext(source);
+        TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+        TestUtils.assertConnectionFails(context, "developer.android.com", 443);
+        TestUtils.assertUrlConnectionFails(context, "google.com", 443);
+        TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+    }
+
+    public void testBasicPinning() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.pins1);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        assertTrue(appConfig.hasPerDomainConfigs());
+        // Check android.com.
+        NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+        PinSet pinSet = config.getPins();
+        assertFalse(pinSet.pins.isEmpty());
+        // Try connections.
+        SSLContext context = TestUtils.getSSLContext(source);
+        TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+        TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+        TestUtils.assertConnectionSucceeds(context, "google.com", 443);
+    }
+
+    public void testExpiredPin() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.expired_pin);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        assertTrue(appConfig.hasPerDomainConfigs());
+        // Check android.com.
+        NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+        PinSet pinSet = config.getPins();
+        assertFalse(pinSet.pins.isEmpty());
+        // Try connections.
+        SSLContext context = TestUtils.getSSLContext(source);
+        TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+        TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+    }
+
+    public void testOverridesPins() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.override_pins);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        assertTrue(appConfig.hasPerDomainConfigs());
+        // Check android.com.
+        NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+        PinSet pinSet = config.getPins();
+        assertFalse(pinSet.pins.isEmpty());
+        // Try connections.
+        SSLContext context = TestUtils.getSSLContext(source);
+        TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+        TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+    }
+
+    public void testBadPin() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.bad_pin);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        assertTrue(appConfig.hasPerDomainConfigs());
+        // Check android.com.
+        NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+        PinSet pinSet = config.getPins();
+        assertFalse(pinSet.pins.isEmpty());
+        // Try connections.
+        SSLContext context = TestUtils.getSSLContext(source);
+        TestUtils.assertConnectionFails(context, "android.com", 443);
+        TestUtils.assertUrlConnectionFails(context, "android.com", 443);
+        TestUtils.assertConnectionSucceeds(context, "google.com", 443);
+    }
+
+    public void testMultipleDomains() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.multiple_domains);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        assertTrue(appConfig.hasPerDomainConfigs());
+        NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+        assertTrue(config.isCleartextTrafficPermitted());
+        assertFalse(config.isHstsEnforced());
+        assertFalse(config.getTrustAnchors().isEmpty());
+        PinSet pinSet = config.getPins();
+        assertTrue(pinSet.pins.isEmpty());
+        // Both android.com and google.com should use the same config
+        NetworkSecurityConfig other = appConfig.getConfigForHostname("google.com");
+        assertEquals(config, other);
+        // Try connections.
+        SSLContext context = TestUtils.getSSLContext(source);
+        TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+        TestUtils.assertConnectionSucceeds(context, "google.com", 443);
+        TestUtils.assertConnectionFails(context, "developer.android.com", 443);
+        TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+    }
+
+    public void testMultipleDomainConfigs() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.multiple_configs);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        assertTrue(appConfig.hasPerDomainConfigs());
+        // Should be two different config objects
+        NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+        NetworkSecurityConfig other = appConfig.getConfigForHostname("google.com");
+        MoreAsserts.assertNotEqual(config, other);
+        // Try connections.
+        SSLContext context = TestUtils.getSSLContext(source);
+        TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+        TestUtils.assertConnectionSucceeds(context, "google.com", 443);
+        TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+    }
+
+    public void testIncludeSubdomains() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.subdomains);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        assertTrue(appConfig.hasPerDomainConfigs());
+        // Try connections.
+        SSLContext context = TestUtils.getSSLContext(source);
+        TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+        TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+        TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+        TestUtils.assertUrlConnectionSucceeds(context, "developer.android.com", 443);
+        TestUtils.assertConnectionFails(context, "google.com", 443);
+    }
+
+    public void testAttributes() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.attributes);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        assertFalse(appConfig.hasPerDomainConfigs());
+        NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+        assertTrue(config.isHstsEnforced());
+        assertFalse(config.isCleartextTrafficPermitted());
+    }
+
+    public void testResourcePemCertificateSource() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.resource_anchors_pem);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        // Check android.com.
+        NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+        assertTrue(config.isCleartextTrafficPermitted());
+        assertFalse(config.isHstsEnforced());
+        assertEquals(2, config.getTrustAnchors().size());
+        // Try connections.
+        SSLContext context = TestUtils.getSSLContext(source);
+        TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+        TestUtils.assertConnectionFails(context, "developer.android.com", 443);
+        TestUtils.assertUrlConnectionFails(context, "google.com", 443);
+        TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+    }
+
+    public void testResourceDerCertificateSource() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.resource_anchors_der);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        // Check android.com.
+        NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+        assertTrue(config.isCleartextTrafficPermitted());
+        assertFalse(config.isHstsEnforced());
+        assertEquals(2, config.getTrustAnchors().size());
+        // Try connections.
+        SSLContext context = TestUtils.getSSLContext(source);
+        TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+        TestUtils.assertConnectionFails(context, "developer.android.com", 443);
+        TestUtils.assertUrlConnectionFails(context, "google.com", 443);
+        TestUtils.assertUrlConnectionSucceeds(context, "android.com", 443);
+    }
+
+    public void testNestedDomainConfigs() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.nested_domains);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        assertTrue(appConfig.hasPerDomainConfigs());
+        NetworkSecurityConfig parent = appConfig.getConfigForHostname("android.com");
+        NetworkSecurityConfig child = appConfig.getConfigForHostname("developer.android.com");
+        MoreAsserts.assertNotEqual(parent, child);
+        MoreAsserts.assertEmpty(parent.getPins().pins);
+        MoreAsserts.assertNotEmpty(child.getPins().pins);
+        // Check that the child inherited the cleartext value and anchors.
+        assertFalse(child.isCleartextTrafficPermitted());
+        MoreAsserts.assertNotEmpty(child.getTrustAnchors());
+        // Test connections.
+        SSLContext context = TestUtils.getSSLContext(source);
+        TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+        TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+    }
+
+    public void testNestedDomainConfigsOverride() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.nested_domains_override);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        assertTrue(appConfig.hasPerDomainConfigs());
+        NetworkSecurityConfig parent = appConfig.getConfigForHostname("android.com");
+        NetworkSecurityConfig child = appConfig.getConfigForHostname("developer.android.com");
+        MoreAsserts.assertNotEqual(parent, child);
+        assertTrue(parent.isCleartextTrafficPermitted());
+        assertFalse(child.isCleartextTrafficPermitted());
+    }
+
+    public void testDebugOverridesDisabled() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_basic, false);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+        Set<TrustAnchor> anchors = config.getTrustAnchors();
+        MoreAsserts.assertEmpty(anchors);
+        SSLContext context = TestUtils.getSSLContext(source);
+        TestUtils.assertConnectionFails(context, "android.com", 443);
+        TestUtils.assertConnectionFails(context, "developer.android.com", 443);
+    }
+
+    public void testBasicDebugOverrides() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_basic, true);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        NetworkSecurityConfig config = appConfig.getConfigForHostname("");
+        Set<TrustAnchor> anchors = config.getTrustAnchors();
+        MoreAsserts.assertNotEmpty(anchors);
+        for (TrustAnchor anchor : anchors) {
+            assertTrue(anchor.overridesPins);
+        }
+        SSLContext context = TestUtils.getSSLContext(source);
+        TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+        TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+    }
+
+    public void testDebugOverridesWithDomain() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_domain, true);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+        Set<TrustAnchor> anchors = config.getTrustAnchors();
+        boolean foundDebugCA = false;
+        for (TrustAnchor anchor : anchors) {
+            if (anchor.certificate.getSubjectDN().toString().equals(DEBUG_CA_SUBJ)) {
+                foundDebugCA = true;
+                assertTrue(anchor.overridesPins);
+            }
+        }
+        assertTrue(foundDebugCA);
+        SSLContext context = TestUtils.getSSLContext(source);
+        TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+        TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+    }
+
+    public void testDebugInherit() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.debug_domain, true);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        NetworkSecurityConfig config = appConfig.getConfigForHostname("android.com");
+        Set<TrustAnchor> anchors = config.getTrustAnchors();
+        boolean foundDebugCA = false;
+        for (TrustAnchor anchor : anchors) {
+            if (anchor.certificate.getSubjectDN().toString().equals(DEBUG_CA_SUBJ)) {
+                foundDebugCA = true;
+                assertTrue(anchor.overridesPins);
+            }
+        }
+        assertTrue(foundDebugCA);
+        assertTrue(anchors.size() > 1);
+        SSLContext context = TestUtils.getSSLContext(source);
+        TestUtils.assertConnectionSucceeds(context, "android.com", 443);
+        TestUtils.assertConnectionSucceeds(context, "developer.android.com", 443);
+    }
+
+    private void testBadConfig(int configId) throws Exception {
+        try {
+            XmlConfigSource source = new XmlConfigSource(getContext(), configId);
+            ApplicationConfig appConfig = new ApplicationConfig(source);
+            appConfig.getConfigForHostname("android.com");
+            fail("Bad config " + getContext().getResources().getResourceName(configId)
+                    + " did not fail to parse");
+        } catch (RuntimeException e) {
+            MoreAsserts.assertAssignableFrom(XmlConfigSource.ParserException.class,
+                    e.getCause());
+        }
+    }
+
+    public void testBadConfig0() throws Exception {
+        testBadConfig(R.xml.bad_config0);
+    }
+
+    public void testBadConfig1() throws Exception {
+        testBadConfig(R.xml.bad_config1);
+    }
+
+    public void testBadConfig2() throws Exception {
+        testBadConfig(R.xml.bad_config2);
+    }
+
+    public void testBadConfig3() throws Exception {
+        testBadConfig(R.xml.bad_config3);
+    }
+
+    public void testBadConfig4() throws Exception {
+        testBadConfig(R.xml.bad_config4);
+    }
+
+    public void testBadConfig5() throws Exception {
+        testBadConfig(R.xml.bad_config4);
+    }
+
+    public void testTrustManagerKeystore() throws Exception {
+        XmlConfigSource source = new XmlConfigSource(getContext(), R.xml.bad_pin, true);
+        ApplicationConfig appConfig = new ApplicationConfig(source);
+        Provider provider = new NetworkSecurityConfigProvider();
+        TrustManagerFactory tmf =
+                TrustManagerFactory.getInstance("PKIX", provider);
+        KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
+        keystore.load(null);
+        int i = 0;
+        for (X509Certificate cert : SystemCertificateSource.getInstance().getCertificates()) {
+            keystore.setEntry(String.valueOf(i),
+                    new KeyStore.TrustedCertificateEntry(cert),
+                    null);
+            i++;
+        }
+        tmf.init(keystore);
+        TrustManager[] tms = tmf.getTrustManagers();
+        SSLContext context = SSLContext.getInstance("TLS");
+        context.init(null, tms, null);
+        TestUtils.assertConnectionSucceeds(context, "android.com" , 443);
+    }
+}