Merge "Camera: fix raw capability check" into lmp-dev
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index cba171f..a72fef6 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -111,6 +111,7 @@
     CtsSaxTestCases \
     CtsSecurityTestCases \
     CtsSpeechTestCases \
+    CtsTelecommTestCases \
     CtsTelephonyTestCases \
     CtsTextTestCases \
     CtsTextureViewTestCases \
diff --git a/libs/testserver/src/android/webkit/cts/TestWebServer.java b/libs/testserver/src/android/webkit/cts/TestWebServer.java
new file mode 100644
index 0000000..9f03939
--- /dev/null
+++ b/libs/testserver/src/android/webkit/cts/TestWebServer.java
@@ -0,0 +1,625 @@
+// Copyright 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Any changes to this file should be done in upstream chromium.org:
+// net/test/android/javatests/src/org/chromium/net/test/util/TestWebServer.java
+
+package android.webkit.cts;
+
+import android.util.Base64;
+import android.util.Log;
+import android.util.Pair;
+
+import org.apache.http.HttpException;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpVersion;
+import org.apache.http.RequestLine;
+import org.apache.http.StatusLine;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.impl.DefaultHttpServerConnection;
+import org.apache.http.impl.cookie.DateUtils;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.CoreProtocolPNames;
+import org.apache.http.params.HttpParams;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * Simple http test server for testing.
+ *
+ * This server runs in a thread in the current process, so it is convenient
+ * for loopback testing without the need to setup tcp forwarding to the
+ * host computer.
+ *
+ * Based heavily on the CTSWebServer in Android.
+ */
+public class TestWebServer {
+    private static final String TAG = "TestWebServer";
+
+    public static final String SHUTDOWN_PREFIX = "/shutdown";
+
+    private static TestWebServer sInstance;
+    private static TestWebServer sSecureInstance;
+    private static Hashtable<Integer, String> sReasons;
+
+    private final ServerThread mServerThread;
+    private String mServerUri;
+    private final boolean mSsl;
+
+    private static class Response {
+        final byte[] mResponseData;
+        final List<Pair<String, String>> mResponseHeaders;
+        final boolean mIsRedirect;
+        final Runnable mResponseAction;
+        final boolean mIsNotFound;
+
+        Response(byte[] responseData, List<Pair<String, String>> responseHeaders,
+                boolean isRedirect, boolean isNotFound, Runnable responseAction) {
+            mIsRedirect = isRedirect;
+            mIsNotFound = isNotFound;
+            mResponseData = responseData;
+            mResponseHeaders = responseHeaders == null ?
+                    new ArrayList<Pair<String, String>>() : responseHeaders;
+            mResponseAction = responseAction;
+        }
+    }
+
+    // The Maps below are modified on both the client thread and the internal server thread, so
+    // need to use a lock when accessing them.
+    private final Object mLock = new Object();
+    private final Map<String, Response> mResponseMap = new HashMap<String, Response>();
+    private final Map<String, Integer> mResponseCountMap = new HashMap<String, Integer>();
+    private final Map<String, HttpRequest> mLastRequestMap = new HashMap<String, HttpRequest>();
+
+    /**
+     * Create and start a local HTTP server instance.
+     * @param ssl True if the server should be using secure sockets.
+     * @throws Exception
+     */
+    public TestWebServer(boolean ssl) throws Exception {
+        mSsl = ssl;
+        if (mSsl) {
+            mServerUri = "https:";
+            if (sSecureInstance != null) {
+                sSecureInstance.shutdown();
+            }
+        } else {
+            mServerUri = "http:";
+            if (sInstance != null) {
+                sInstance.shutdown();
+            }
+        }
+
+        setInstance(this, mSsl);
+        mServerThread = new ServerThread(this, mSsl);
+        mServerThread.start();
+        mServerUri += "//localhost:" + mServerThread.mSocket.getLocalPort();
+    }
+
+    /**
+     * Terminate the http server.
+     */
+    public void shutdown() {
+        try {
+            // Avoid a deadlock between two threads where one is trying to call
+            // close() and the other one is calling accept() by sending a GET
+            // request for shutdown and having the server's one thread
+            // sequentially call accept() and close().
+            URL url = new URL(mServerUri + SHUTDOWN_PREFIX);
+            URLConnection connection = openConnection(url);
+            connection.connect();
+
+            // Read the input from the stream to send the request.
+            InputStream is = connection.getInputStream();
+            is.close();
+
+            // Block until the server thread is done shutting down.
+            mServerThread.join();
+
+        } catch (MalformedURLException e) {
+            throw new IllegalStateException(e);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } catch (NoSuchAlgorithmException e) {
+            throw new IllegalStateException(e);
+        } catch (KeyManagementException e) {
+            throw new IllegalStateException(e);
+        }
+
+        setInstance(null, mSsl);
+    }
+
+    private static void setInstance(TestWebServer instance, boolean isSsl) {
+        if (isSsl) {
+            sSecureInstance = instance;
+        } else {
+            sInstance = instance;
+        }
+    }
+
+    private static final int RESPONSE_STATUS_NORMAL = 0;
+    private static final int RESPONSE_STATUS_MOVED_TEMPORARILY = 1;
+    private static final int RESPONSE_STATUS_NOT_FOUND = 2;
+
+    private String setResponseInternal(
+            String requestPath, byte[] responseData,
+            List<Pair<String, String>> responseHeaders, Runnable responseAction,
+            int status) {
+        final boolean isRedirect = (status == RESPONSE_STATUS_MOVED_TEMPORARILY);
+        final boolean isNotFound = (status == RESPONSE_STATUS_NOT_FOUND);
+
+        synchronized (mLock) {
+            mResponseMap.put(requestPath, new Response(
+                    responseData, responseHeaders, isRedirect, isNotFound, responseAction));
+            mResponseCountMap.put(requestPath, Integer.valueOf(0));
+            mLastRequestMap.put(requestPath, null);
+        }
+        return getResponseUrl(requestPath);
+    }
+
+    /**
+     * Gets the URL on the server under which a particular request path will be accessible.
+     *
+     * This only gets the URL, you still need to set the response if you intend to access it.
+     *
+     * @param requestPath The path to respond to.
+     * @return The full URL including the requestPath.
+     */
+    public String getResponseUrl(String requestPath) {
+        return mServerUri + requestPath;
+    }
+
+    /**
+     * Sets a 404 (not found) response to be returned when a particular request path is passed in.
+     *
+     * @param requestPath The path to respond to.
+     * @return The full URL including the path that should be requested to get the expected
+     *         response.
+     */
+    public String setResponseWithNotFoundStatus(
+            String requestPath) {
+        return setResponseInternal(requestPath, "".getBytes(), null, null,
+                RESPONSE_STATUS_NOT_FOUND);
+    }
+
+    /**
+     * Sets a response to be returned when a particular request path is passed
+     * in (with the option to specify additional headers).
+     *
+     * @param requestPath The path to respond to.
+     * @param responseString The response body that will be returned.
+     * @param responseHeaders Any additional headers that should be returned along with the
+     *                        response (null is acceptable).
+     * @return The full URL including the path that should be requested to get the expected
+     *         response.
+     */
+    public String setResponse(
+            String requestPath, String responseString,
+            List<Pair<String, String>> responseHeaders) {
+        return setResponseInternal(requestPath, responseString.getBytes(), responseHeaders, null,
+                RESPONSE_STATUS_NORMAL);
+    }
+
+    /**
+     * Sets a response to be returned when a particular request path is passed
+     * in with the option to specify additional headers as well as an arbitrary action to be
+     * executed on each request.
+     *
+     * @param requestPath The path to respond to.
+     * @param responseString The response body that will be returned.
+     * @param responseHeaders Any additional headers that should be returned along with the
+     *                        response (null is acceptable).
+     * @param responseAction The action to be performed when fetching the response.  This action
+     *                       will be executed for each request and will be handled on a background
+     *                       thread.
+     * @return The full URL including the path that should be requested to get the expected
+     *         response.
+     */
+    public String setResponseWithRunnableAction(
+            String requestPath, String responseString, List<Pair<String, String>> responseHeaders,
+            Runnable responseAction) {
+        return setResponseInternal(
+                requestPath, responseString.getBytes(), responseHeaders, responseAction,
+                RESPONSE_STATUS_NORMAL);
+    }
+
+    /**
+     * Sets a redirect.
+     *
+     * @param requestPath The path to respond to.
+     * @param targetPath The path to redirect to.
+     * @return The full URL including the path that should be requested to get the expected
+     *         response.
+     */
+    public String setRedirect(
+            String requestPath, String targetPath) {
+        List<Pair<String, String>> responseHeaders = new ArrayList<Pair<String, String>>();
+        responseHeaders.add(Pair.create("Location", targetPath));
+
+        return setResponseInternal(requestPath, targetPath.getBytes(), responseHeaders, null,
+                RESPONSE_STATUS_MOVED_TEMPORARILY);
+    }
+
+    /**
+     * Sets a base64 encoded response to be returned when a particular request path is passed
+     * in (with the option to specify additional headers).
+     *
+     * @param requestPath The path to respond to.
+     * @param base64EncodedResponse The response body that is base64 encoded. The actual server
+     *                              response will the decoded binary form.
+     * @param responseHeaders Any additional headers that should be returned along with the
+     *                        response (null is acceptable).
+     * @return The full URL including the path that should be requested to get the expected
+     *         response.
+     */
+    public String setResponseBase64(
+            String requestPath, String base64EncodedResponse,
+            List<Pair<String, String>> responseHeaders) {
+        return setResponseInternal(
+                requestPath, Base64.decode(base64EncodedResponse, Base64.DEFAULT),
+                responseHeaders, null, RESPONSE_STATUS_NORMAL);
+    }
+
+    /**
+     * Get the number of requests was made at this path since it was last set.
+     */
+    public int getRequestCount(String requestPath) {
+        Integer count = null;
+        synchronized (mLock) {
+            count = mResponseCountMap.get(requestPath);
+        }
+        if (count == null) throw new IllegalArgumentException("Path not set: " + requestPath);
+        return count.intValue();
+    }
+
+    /**
+     * Returns the last HttpRequest at this path. Can return null if it is never requested.
+     */
+    public HttpRequest getLastRequest(String requestPath) {
+        synchronized (mLock) {
+            if (!mLastRequestMap.containsKey(requestPath))
+                throw new IllegalArgumentException("Path not set: " + requestPath);
+            return mLastRequestMap.get(requestPath);
+        }
+    }
+
+    public String getBaseUrl() {
+        return mServerUri + "/";
+    }
+
+    private URLConnection openConnection(URL url)
+            throws IOException, NoSuchAlgorithmException, KeyManagementException {
+        if (mSsl) {
+            // Install hostname verifiers and trust managers that don't do
+            // anything in order to get around the client not trusting
+            // the test server due to a lack of certificates.
+
+            HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
+            connection.setHostnameVerifier(new TestHostnameVerifier());
+
+            SSLContext context = SSLContext.getInstance("TLS");
+            TestTrustManager trustManager = new TestTrustManager();
+            context.init(null, new TestTrustManager[] {trustManager}, null);
+            connection.setSSLSocketFactory(context.getSocketFactory());
+
+            return connection;
+        } else {
+            return url.openConnection();
+        }
+    }
+
+    /**
+     * {@link X509TrustManager} that trusts everybody. This is used so that
+     * the client calling {@link TestWebServer#shutdown()} can issue a request
+     * for shutdown by blindly trusting the {@link TestWebServer}'s
+     * credentials.
+     */
+    private static class TestTrustManager implements X509TrustManager {
+        @Override
+        public void checkClientTrusted(X509Certificate[] chain, String authType) {
+            // Trust the TestWebServer...
+        }
+
+        @Override
+        public void checkServerTrusted(X509Certificate[] chain, String authType) {
+            // Trust the TestWebServer...
+        }
+
+        @Override
+        public X509Certificate[] getAcceptedIssuers() {
+            return null;
+        }
+    }
+
+    /**
+     * {@link HostnameVerifier} that verifies everybody. This permits
+     * the client to trust the web server and call
+     * {@link TestWebServer#shutdown()}.
+     */
+    private static class TestHostnameVerifier implements HostnameVerifier {
+        @Override
+        public boolean verify(String hostname, SSLSession session) {
+            return true;
+        }
+    }
+
+    private void servedResponseFor(String path, HttpRequest request) {
+        synchronized (mLock) {
+            mResponseCountMap.put(path, Integer.valueOf(
+                    mResponseCountMap.get(path).intValue() + 1));
+            mLastRequestMap.put(path, request);
+        }
+    }
+
+    /**
+     * Generate a response to the given request.
+     *
+     * <p>Always executed on the background server thread.
+     *
+     * <p>If there is an action associated with the response, it will be executed inside of
+     * this function.
+     *
+     * @throws InterruptedException
+     */
+    private HttpResponse getResponse(HttpRequest request) throws InterruptedException {
+        assert Thread.currentThread() == mServerThread
+                : "getResponse called from non-server thread";
+
+        RequestLine requestLine = request.getRequestLine();
+        HttpResponse httpResponse = null;
+        Log.i(TAG, requestLine.getMethod() + ": " + requestLine.getUri());
+        String uriString = requestLine.getUri();
+        URI uri = URI.create(uriString);
+        String path = uri.getPath();
+
+        Response response = null;
+        synchronized (mLock) {
+            response = mResponseMap.get(path);
+        }
+        if (path.equals(SHUTDOWN_PREFIX)) {
+            httpResponse = createResponse(HttpStatus.SC_OK);
+        } else if (response == null) {
+            httpResponse = createResponse(HttpStatus.SC_NOT_FOUND);
+        } else if (response.mIsNotFound) {
+            httpResponse = createResponse(HttpStatus.SC_NOT_FOUND);
+            servedResponseFor(path, request);
+        } else if (response.mIsRedirect) {
+            httpResponse = createResponse(HttpStatus.SC_MOVED_TEMPORARILY);
+            for (Pair<String, String> header : response.mResponseHeaders) {
+                httpResponse.addHeader(header.first, header.second);
+            }
+            servedResponseFor(path, request);
+        } else {
+            if (response.mResponseAction != null) response.mResponseAction.run();
+
+            httpResponse = createResponse(HttpStatus.SC_OK);
+            ByteArrayEntity entity = createEntity(response.mResponseData);
+            httpResponse.setEntity(entity);
+            httpResponse.setHeader("Content-Length", "" + entity.getContentLength());
+            for (Pair<String, String> header : response.mResponseHeaders) {
+                httpResponse.addHeader(header.first, header.second);
+            }
+            servedResponseFor(path, request);
+        }
+        StatusLine sl = httpResponse.getStatusLine();
+        Log.i(TAG, sl.getStatusCode() + "(" + sl.getReasonPhrase() + ")");
+        setDateHeaders(httpResponse);
+        return httpResponse;
+    }
+
+    private void setDateHeaders(HttpResponse response) {
+        response.addHeader("Date", DateUtils.formatDate(new Date(), DateUtils.PATTERN_RFC1123));
+    }
+
+    /**
+     * Create an empty response with the given status.
+     */
+    private HttpResponse createResponse(int status) {
+        HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_0, status, null);
+        String reason = null;
+
+        // This synchronized silences findbugs.
+        synchronized (TestWebServer.class) {
+            if (sReasons == null) {
+                sReasons = new Hashtable<Integer, String>();
+                sReasons.put(HttpStatus.SC_UNAUTHORIZED, "Unauthorized");
+                sReasons.put(HttpStatus.SC_NOT_FOUND, "Not Found");
+                sReasons.put(HttpStatus.SC_FORBIDDEN, "Forbidden");
+                sReasons.put(HttpStatus.SC_MOVED_TEMPORARILY, "Moved Temporarily");
+            }
+            // Fill in error reason. Avoid use of the ReasonPhraseCatalog, which is
+            // Locale-dependent.
+            reason = sReasons.get(status);
+        }
+
+        if (reason != null) {
+            StringBuffer buf = new StringBuffer("<html><head><title>");
+            buf.append(reason);
+            buf.append("</title></head><body>");
+            buf.append(reason);
+            buf.append("</body></html>");
+            ByteArrayEntity entity = createEntity(buf.toString().getBytes());
+            response.setEntity(entity);
+            response.setHeader("Content-Length", "" + entity.getContentLength());
+        }
+        return response;
+    }
+
+    /**
+     * Create a string entity for the given content.
+     */
+    private ByteArrayEntity createEntity(byte[] data) {
+        ByteArrayEntity entity = new ByteArrayEntity(data);
+        entity.setContentType("text/html");
+        return entity;
+    }
+
+    private static class ServerThread extends Thread {
+        private TestWebServer mServer;
+        private ServerSocket mSocket;
+        private boolean mIsSsl;
+        private boolean mIsCancelled;
+        private SSLContext mSslContext;
+
+        /**
+         * Defines the keystore contents for the server, BKS version. Holds just a
+         * single self-generated key. The subject name is "Test Server".
+         */
+        private static final String SERVER_KEYS_BKS =
+            "AAAAAQAAABQDkebzoP1XwqyWKRCJEpn/t8dqIQAABDkEAAVteWtleQAAARpYl20nAAAAAQAFWC41" +
+            "MDkAAAJNMIICSTCCAbKgAwIBAgIESEfU1jANBgkqhkiG9w0BAQUFADBpMQswCQYDVQQGEwJVUzET" +
+            "MBEGA1UECBMKQ2FsaWZvcm5pYTEMMAoGA1UEBxMDTVRWMQ8wDQYDVQQKEwZHb29nbGUxEDAOBgNV" +
+            "BAsTB0FuZHJvaWQxFDASBgNVBAMTC1Rlc3QgU2VydmVyMB4XDTA4MDYwNTExNTgxNFoXDTA4MDkw" +
+            "MzExNTgxNFowaTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExDDAKBgNVBAcTA01U" +
+            "VjEPMA0GA1UEChMGR29vZ2xlMRAwDgYDVQQLEwdBbmRyb2lkMRQwEgYDVQQDEwtUZXN0IFNlcnZl" +
+            "cjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA0LIdKaIr9/vsTq8BZlA3R+NFWRaH4lGsTAQy" +
+            "DPMF9ZqEDOaL6DJuu0colSBBBQ85hQTPa9m9nyJoN3pEi1hgamqOvQIWcXBk+SOpUGRZZFXwniJV" +
+            "zDKU5nE9MYgn2B9AoiH3CSuMz6HRqgVaqtppIe1jhukMc/kHVJvlKRNy9XMCAwEAATANBgkqhkiG" +
+            "9w0BAQUFAAOBgQC7yBmJ9O/eWDGtSH9BH0R3dh2NdST3W9hNZ8hIa8U8klhNHbUCSSktZmZkvbPU" +
+            "hse5LI3dh6RyNDuqDrbYwcqzKbFJaq/jX9kCoeb3vgbQElMRX8D2ID1vRjxwlALFISrtaN4VpWzV" +
+            "yeoHPW4xldeZmoVtjn8zXNzQhLuBqX2MmAAAAqwAAAAUvkUScfw9yCSmALruURNmtBai7kQAAAZx" +
+            "4Jmijxs/l8EBaleaUru6EOPioWkUAEVWCxjM/TxbGHOi2VMsQWqRr/DZ3wsDmtQgw3QTrUK666sR" +
+            "MBnbqdnyCyvM1J2V1xxLXPUeRBmR2CXorYGF9Dye7NkgVdfA+9g9L/0Au6Ugn+2Cj5leoIgkgApN" +
+            "vuEcZegFlNOUPVEs3SlBgUF1BY6OBM0UBHTPwGGxFBBcetcuMRbUnu65vyDG0pslT59qpaR0TMVs" +
+            "P+tcheEzhyjbfM32/vwhnL9dBEgM8qMt0sqF6itNOQU/F4WGkK2Cm2v4CYEyKYw325fEhzTXosck" +
+            "MhbqmcyLab8EPceWF3dweoUT76+jEZx8lV2dapR+CmczQI43tV9btsd1xiBbBHAKvymm9Ep9bPzM" +
+            "J0MQi+OtURL9Lxke/70/MRueqbPeUlOaGvANTmXQD2OnW7PISwJ9lpeLfTG0LcqkoqkbtLKQLYHI" +
+            "rQfV5j0j+wmvmpMxzjN3uvNajLa4zQ8l0Eok9SFaRr2RL0gN8Q2JegfOL4pUiHPsh64WWya2NB7f" +
+            "V+1s65eA5ospXYsShRjo046QhGTmymwXXzdzuxu8IlnTEont6P4+J+GsWk6cldGbl20hctuUKzyx" +
+            "OptjEPOKejV60iDCYGmHbCWAzQ8h5MILV82IclzNViZmzAapeeCnexhpXhWTs+xDEYSKEiG/camt" +
+            "bhmZc3BcyVJrW23PktSfpBQ6D8ZxoMfF0L7V2GQMaUg+3r7ucrx82kpqotjv0xHghNIm95aBr1Qw" +
+            "1gaEjsC/0wGmmBDg1dTDH+F1p9TInzr3EFuYD0YiQ7YlAHq3cPuyGoLXJ5dXYuSBfhDXJSeddUkl" +
+            "k1ufZyOOcskeInQge7jzaRfmKg3U94r+spMEvb0AzDQVOKvjjo1ivxMSgFRZaDb/4qw=";
+
+        private static final String PASSWORD = "android";
+
+        /**
+         * Loads a keystore from a base64-encoded String. Returns the KeyManager[]
+         * for the result.
+         */
+        private KeyManager[] getKeyManagers() throws Exception {
+            byte[] bytes = Base64.decode(SERVER_KEYS_BKS, Base64.DEFAULT);
+            InputStream inputStream = new ByteArrayInputStream(bytes);
+
+            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+            keyStore.load(inputStream, PASSWORD.toCharArray());
+            inputStream.close();
+
+            String algorithm = KeyManagerFactory.getDefaultAlgorithm();
+            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(algorithm);
+            keyManagerFactory.init(keyStore, PASSWORD.toCharArray());
+
+            return keyManagerFactory.getKeyManagers();
+        }
+
+
+        public ServerThread(TestWebServer server, boolean ssl) throws Exception {
+            super("ServerThread");
+            mServer = server;
+            mIsSsl = ssl;
+            int retry = 3;
+            while (true) {
+                try {
+                    if (mIsSsl) {
+                        mSslContext = SSLContext.getInstance("TLS");
+                        mSslContext.init(getKeyManagers(), null, null);
+                        mSocket = mSslContext.getServerSocketFactory().createServerSocket(0);
+                    } else {
+                        mSocket = new ServerSocket(0);
+                    }
+                    return;
+                } catch (IOException e) {
+                    Log.w(TAG, e);
+                    if (--retry == 0) {
+                        throw e;
+                    }
+                    // sleep in case server socket is still being closed
+                    Thread.sleep(1000);
+                }
+            }
+        }
+
+        @Override
+        public void run() {
+            HttpParams params = new BasicHttpParams();
+            params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_0);
+            while (!mIsCancelled) {
+                try {
+                    Socket socket = mSocket.accept();
+                    DefaultHttpServerConnection conn = new DefaultHttpServerConnection();
+                    conn.bind(socket, params);
+
+                    // Determine whether we need to shutdown early before
+                    // parsing the response since conn.close() will crash
+                    // for SSL requests due to UnsupportedOperationException.
+                    HttpRequest request = conn.receiveRequestHeader();
+                    if (isShutdownRequest(request)) {
+                        mIsCancelled = true;
+                    }
+
+                    HttpResponse response = mServer.getResponse(request);
+                    conn.sendResponseHeader(response);
+                    conn.sendResponseEntity(response);
+                    conn.close();
+
+                } catch (IOException e) {
+                    // normal during shutdown, ignore
+                    Log.w(TAG, e);
+                } catch (HttpException e) {
+                    Log.w(TAG, e);
+                } catch (InterruptedException e) {
+                    Log.w(TAG, e);
+                } catch (UnsupportedOperationException e) {
+                    // DefaultHttpServerConnection's close() throws an
+                    // UnsupportedOperationException.
+                    Log.w(TAG, e);
+                }
+            }
+            try {
+                mSocket.close();
+            } catch (IOException ignored) {
+                // safe to ignore
+            }
+        }
+
+        private boolean isShutdownRequest(HttpRequest request) {
+            RequestLine requestLine = request.getRequestLine();
+            String uriString = requestLine.getUri();
+            URI uri = URI.create(uriString);
+            String path = uri.getPath();
+            return path.equals(SHUTDOWN_PREFIX);
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
index 7f50c5a..7ddae65 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -33,6 +33,7 @@
 import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
+import android.hardware.camera2.params.MeteringRectangle;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.util.Log;
@@ -989,6 +990,25 @@
     }
 
     /**
+     * <p>Check if 3A metering settings are "up to HAL" in request template</p>
+     *
+     * <p>This function doesn't fail the test immediately, it updates the
+     * test pass/fail status and appends the failure message to the error collector each key.</p>
+     *
+     * @param regions The metering rectangles to be checked
+     */
+    private void checkMeteringRect(MeteringRectangle[] regions) {
+        if (regions == null) {
+            return;
+        }
+        mCollector.expectNotEquals("Number of metering region should not be 0", 0, regions.length);
+        for (int i = 0; i < regions.length; i++) {
+            mCollector.expectEquals("Default metering regions should have all zero weight",
+                    0, regions[i].getMeteringWeight());
+        }
+    }
+
+    /**
      * <p>Check if the request settings are suitable for a given request template.</p>
      *
      * <p>This function doesn't fail the test immediately, it updates the
@@ -1044,12 +1064,18 @@
             }
             if (maxRegionsAe > 0) {
                 mCollector.expectKeyValueNotNull(request, CONTROL_AE_REGIONS);
+                MeteringRectangle[] aeRegions = request.get(CONTROL_AE_REGIONS);
+                checkMeteringRect(aeRegions);
             }
             if (maxRegionsAwb > 0) {
                 mCollector.expectKeyValueNotNull(request, CONTROL_AWB_REGIONS);
+                MeteringRectangle[] awbRegions = request.get(CONTROL_AWB_REGIONS);
+                checkMeteringRect(awbRegions);
             }
             if (maxRegionsAf > 0) {
                 mCollector.expectKeyValueNotNull(request, CONTROL_AF_REGIONS);
+                MeteringRectangle[] afRegions = request.get(CONTROL_AF_REGIONS);
+                checkMeteringRect(afRegions);
             }
         }
 
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
index ec79151..6856438 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -1600,7 +1600,7 @@
     }
 
     private void digitalZoomTestByCamera(Size previewSize) throws Exception {
-        final int ZOOM_STEPS = 30;
+        final int ZOOM_STEPS = 15;
         final PointF[] TEST_ZOOM_CENTERS;
 
         final int croppingType = mStaticInfo.getScalerCroppingTypeChecked();
@@ -1635,7 +1635,10 @@
         CaptureRequest.Builder requestBuilder =
                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
         SimpleCaptureListener listener = new SimpleCaptureListener();
-        startPreview(requestBuilder, previewSize, listener);
+
+        updatePreviewSurface(previewSize);
+        configurePreviewOutput(requestBuilder);
+
         CaptureRequest[] requests = new CaptureRequest[ZOOM_STEPS];
 
         // Set algorithm regions to full active region
@@ -1681,6 +1684,9 @@
                 requestBuilder.set(CaptureRequest.SCALER_CROP_REGION, cropRegions[i]);
                 requests[i] = requestBuilder.build();
                 for (int j = 0; j < CAPTURE_SUBMIT_REPEAT; ++j) {
+                    if (VERBOSE) {
+                        Log.v(TAG, "submit crop region " + cropRegions[i]);
+                    }
                     mSession.capture(requests[i], listener, mHandler);
                 }
 
@@ -1746,20 +1752,46 @@
                              previousCrop.height() < activeArraySize.height()));
             }
         }
-
-        stopPreview();
     }
 
     private void digitalZoomPreviewCombinationTestByCamera() throws Exception {
+        final double ASPECT_RATIO_THRESHOLD = 0.001;
+        List<Double> aspectRatiosTested = new ArrayList<Double>();
+        Size maxPreviewSize = mOrderedPreviewSizes.get(0);
+        aspectRatiosTested.add((double)(maxPreviewSize.getWidth()) / maxPreviewSize.getHeight());
+
         for (Size size : mOrderedPreviewSizes) {
+            // Max preview size was already tested in testDigitalZoom test. skip it.
+            if (size.equals(maxPreviewSize)) {
+                continue;
+            }
+
+            // Only test the largest size for each aspect ratio.
+            double aspectRatio = (double)(size.getWidth()) / size.getHeight();
+            if (isAspectRatioContained(aspectRatiosTested, aspectRatio, ASPECT_RATIO_THRESHOLD)) {
+                continue;
+            }
+
             if (VERBOSE) {
                 Log.v(TAG, "Test preview size " + size.toString() + " digital zoom");
             }
 
+            aspectRatiosTested.add(aspectRatio);
             digitalZoomTestByCamera(size);
         }
     }
 
+    private static boolean isAspectRatioContained(List<Double> aspectRatioList,
+            double aspectRatio, double delta) {
+        for (Double ratio : aspectRatioList) {
+            if (Math.abs(ratio - aspectRatio) < delta) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     private void sceneModeTestByCamera() throws Exception {
         int[] sceneModes = mStaticInfo.getAvailableSceneModesChecked();
         Size maxPreviewSize = mOrderedPreviewSizes.get(0);
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index 61998e7..c31c484 100755
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -31,7 +31,9 @@
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.FileReader;
+import java.io.InputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executors;
@@ -249,6 +251,18 @@
         assertFileOwnedByGroup(f, "root");
     }
 
+    @MediumTest
+    public void testIdletimerDirectoryExistsAndSane() throws Exception {
+        File dir = new File("/sys/class/xt_idletimer");
+        assertTrue(dir.isDirectory());
+        assertTrue(dir.canRead());
+        assertFalse(dir.canWrite());
+        assertTrue(dir.canExecute());
+
+        assertFileOwnedBy(dir, "root");
+        assertFileOwnedByGroup(dir, "root");
+    }
+
     /**
      * Assert that a file is owned by a specific owner. This is a noop if the
      * file does not exist.
@@ -815,19 +829,29 @@
     }
 
     public void testDevRandomWorldReadableAndWritable() throws Exception {
+        File f = new File("/dev/random");
+
+        assertTrue(f + " cannot be opened for reading", canOpenForReading(f));
+        assertTrue(f + " cannot be opened for writing", canOpenForWriting(f));
+
         FileUtils.FileStatus status = new FileUtils.FileStatus();
-        assertTrue(FileUtils.getFileStatus("/dev/random", status, false));
+        assertTrue(FileUtils.getFileStatus(f.getPath(), status, false));
         assertTrue(
-                "/dev/random not world-readable/writable. Actual mode: 0"
+                f + " not world-readable/writable. Actual mode: 0"
                         + Integer.toString(status.mode, 8),
                 (status.mode & 0666) == 0666);
     }
 
     public void testDevUrandomWorldReadableAndWritable() throws Exception {
+        File f = new File("/dev/urandom");
+
+        assertTrue(f + " cannot be opened for reading", canOpenForReading(f));
+        assertTrue(f + " cannot be opened for writing", canOpenForWriting(f));
+
         FileUtils.FileStatus status = new FileUtils.FileStatus();
-        assertTrue(FileUtils.getFileStatus("/dev/urandom", status, false));
+        assertTrue(FileUtils.getFileStatus(f.getPath(), status, false));
         assertTrue(
-                "/dev/urandom not world-readable/writable. Actual mode: 0"
+                f + " not world-readable/writable. Actual mode: 0"
                         + Integer.toString(status.mode, 8),
                 (status.mode & 0666) == 0666);
     }
@@ -839,15 +863,28 @@
             return;
         }
 
-        FileUtils.FileStatus status = new FileUtils.FileStatus();
-        assertTrue(FileUtils.getFileStatus(f.getCanonicalPath(), status, false));
-        assertTrue(
-                f + " has wrong file mode: 0"
-                        + Integer.toOctalString(status.mode),
-                (status.mode & 0777) == 0440);
+        assertFalse(f + " can be opened for reading", canOpenForReading(f));
+        assertFalse(f + " can be opened for writing", canOpenForWriting(f));
 
-        assertFileOwnedBy(f, "root");
-        assertFileOwnedByGroup(f, "system");
+        FileUtils.FileStatus status = new FileUtils.FileStatus();
+        assertFalse("stat permitted on " + f,
+                FileUtils.getFileStatus(f.getPath(), status, false));
+    }
+
+    private static boolean canOpenForReading(File f) {
+        try (InputStream in = new FileInputStream(f)) {
+            return true;
+        } catch (IOException expected) {
+            return false;
+        }
+    }
+
+    private static boolean canOpenForWriting(File f) {
+        try (OutputStream out = new FileOutputStream(f)) {
+            return true;
+        } catch (IOException expected) {
+            return false;
+        }
     }
 
     public void testFileHasOnlyCapsThrowsOnInvalidCaps() throws Exception {
diff --git a/tests/tests/telecomm/Android.mk b/tests/tests/telecomm/Android.mk
new file mode 100644
index 0000000..70ed780
--- /dev/null
+++ b/tests/tests/telecomm/Android.mk
@@ -0,0 +1,39 @@
+# Copyright (C) 2014 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.
+
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+#LOCAL_JAVA_LIBRARIES := Telecomm
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsTelecommTestCases
+
+LOCAL_INSTRUMENTATION_FOR := CtsTestStubs
+
+# uncomment when b/13250611 is fixed
+#LOCAL_SDK_VERSION := current
+LOCAL_JAVA_LIBRARIES += android.test.runner
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/telecomm/AndroidManifest.xml b/tests/tests/telecomm/AndroidManifest.xml
new file mode 100644
index 0000000..9f7b307
--- /dev/null
+++ b/tests/tests/telecomm/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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="com.android.cts.telecomm">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <!-- We won't be granted this, but we should at least ask for it (to make sure we don't get it). -->
+    <uses-permission android:name="com.android.telecomm.permission.REGISTER_PROVIDER_OR_SUBSCRIPTION" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.cts.stub"
+                     android:label="CTS tests of android.telecomm">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
+
diff --git a/tests/tests/telecomm/src/android/telecomm/cts/ConnectionTest.java b/tests/tests/telecomm/src/android/telecomm/cts/ConnectionTest.java
new file mode 100644
index 0000000..0883cff
--- /dev/null
+++ b/tests/tests/telecomm/src/android/telecomm/cts/ConnectionTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2014 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.telecomm.cts;
+
+import android.telecomm.Connection;
+import android.telephony.DisconnectCause;
+import android.test.AndroidTestCase;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+public class ConnectionTest extends AndroidTestCase {
+    public void testStateCallbacks() {
+        final Semaphore lock = new Semaphore(0);
+        Connection connection = createConnection(lock);
+
+        waitForStateChange(lock);
+        assertEquals(Connection.STATE_NEW, connection.getState());
+
+        connection.setInitializing();
+        waitForStateChange(lock);
+        assertEquals(Connection.STATE_INITIALIZING, connection.getState());
+
+        connection.setInitialized();
+        waitForStateChange(lock);
+        assertEquals(Connection.STATE_NEW, connection.getState());
+
+        connection.setRinging();
+        waitForStateChange(lock);
+        assertEquals(Connection.STATE_RINGING, connection.getState());
+
+        connection.setDialing();
+        waitForStateChange(lock);
+        assertEquals(Connection.STATE_DIALING, connection.getState());
+
+        connection.setActive();
+        waitForStateChange(lock);
+        assertEquals(Connection.STATE_ACTIVE, connection.getState());
+
+        connection.setOnHold();
+        waitForStateChange(lock);
+        assertEquals(Connection.STATE_HOLDING, connection.getState());
+
+        connection.setDisconnected(DisconnectCause.LOCAL, "Test call");
+        waitForStateChange(lock);
+        assertEquals(Connection.STATE_DISCONNECTED, connection.getState());
+
+        connection.setRinging();
+        waitForStateChange(lock);
+        assertEquals("Connection should not move out of STATE_DISCONNECTED.",
+                Connection.STATE_DISCONNECTED, connection.getState());
+    }
+
+    public void testFailedState() {
+        Connection connection =
+                Connection.createFailedConnection(DisconnectCause.LOCAL, "Test call");
+        assertEquals(Connection.STATE_DISCONNECTED, connection.getState());
+
+        connection.setRinging();
+        assertEquals("Connection should not move out of STATE_DISCONNECTED.",
+                Connection.STATE_DISCONNECTED, connection.getState());
+    }
+
+    public void testCanceledState() {
+        Connection connection = Connection.createCanceledConnection();
+        assertEquals(Connection.STATE_DISCONNECTED, connection.getState());
+
+        connection.setDialing();
+        assertEquals("Connection should not move out of STATE_DISCONNECTED",
+                Connection.STATE_DISCONNECTED, connection.getState());
+    }
+
+    private static Connection createConnection(final Semaphore lock) {
+        Connection.Listener listener = new Connection.Listener() {
+            @Override
+            public void onStateChanged(Connection c, int state) {
+                lock.release();
+            }
+        };
+
+        Connection connection = new BasicConnection();
+        connection.addConnectionListener(listener);
+        return connection;
+    }
+
+    private static void waitForStateChange(Semaphore lock) {
+        try {
+            lock.tryAcquire(1000, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            fail("State transition timed out");
+        }
+    }
+
+    private static final class BasicConnection extends Connection {
+    }
+}
diff --git a/tests/tests/telecomm/src/android/telecomm/cts/TelecommManagerTest.java b/tests/tests/telecomm/src/android/telecomm/cts/TelecommManagerTest.java
new file mode 100644
index 0000000..7f8e991
--- /dev/null
+++ b/tests/tests/telecomm/src/android/telecomm/cts/TelecommManagerTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 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.telecomm.cts;
+
+import android.content.ComponentName;
+import android.net.Uri;
+import android.telecomm.PhoneAccount;
+import android.telecomm.PhoneAccountHandle;
+import android.telecomm.PhoneCapabilities;
+import android.telecomm.TelecommManager;
+import android.test.AndroidTestCase;
+
+import java.util.List;
+
+public class TelecommManagerTest extends AndroidTestCase {
+    public void testRegisterAccountsBlocked() {
+        PhoneAccount phoneAccount = PhoneAccount.builder()
+                .withAccountHandle(new PhoneAccountHandle(
+                                new ComponentName(getContext(), TelecommManagerTest.class),
+                                "testRegisterAccountsBlocked"))
+                .withHandle(Uri.parse("tel:6502637643"))
+                .withSubscriptionNumber("650-263-7643")
+                .withCapabilities(PhoneCapabilities.ALL)
+                .withIconResId(0)
+                .withLabel("Mock PhoneAccount")
+                .withShortDescription("PhoneAccount used in TelecommManagerTest")
+                .build();
+
+        TelecommManager tm = TelecommManager.from(getContext());
+        List<PhoneAccountHandle> handles = tm.getEnabledPhoneAccounts();
+
+        try {
+            tm.registerPhoneAccount(phoneAccount);
+            fail("This should have failed (CTS can't get the permission)");
+        } catch (SecurityException e) {
+            assertEquals(handles, tm.getEnabledPhoneAccounts());
+        }
+    }
+}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
index c2104fe..1515a8a 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -18,15 +18,19 @@
 import android.content.Context;
 import android.cts.util.PollingCheck;
 import android.graphics.Bitmap;
+import android.net.http.SslError;
 import android.os.Build;
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
 import android.webkit.ConsoleMessage;
+import android.webkit.SslErrorHandler;
 import android.webkit.WebIconDatabase;
 import android.webkit.WebSettings;
 import android.webkit.WebSettings.TextSize;
 import android.webkit.WebStorage;
 import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.webkit.cts.WebViewOnUiThread.WaitForLoadedClient;
 import android.webkit.cts.WebViewOnUiThread.WaitForProgressClient;
 import java.io.FileOutputStream;
 import java.util.Locale;
@@ -932,6 +936,71 @@
         fos.close();
     }
 
+    public void testAllowMixedMode() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+        final class SslWebViewClient extends WaitForLoadedClient {
+            public SslWebViewClient() {
+                super(mOnUiThread);
+            }
+            @Override
+            public void onReceivedSslError(WebView view,
+                    SslErrorHandler handler, SslError error) {
+                handler.proceed();
+            }
+        }
+
+        mSettings.setJavaScriptEnabled(true);
+        TestWebServer httpsServer = null;
+        TestWebServer httpServer = null;
+        try {
+            httpsServer = new TestWebServer(true);
+            httpServer = new TestWebServer(false);
+            final String JS_URL = "/insecure.js";
+            final String IMG_URL = "/insecure.png";
+            final String SECURE_URL = "/secure.html";
+            final String JS_HTML = "<script src=\"" + httpServer.getResponseUrl(JS_URL) +
+                "\"></script>";
+            final String IMG_HTML = "<img src=\"" + httpServer.getResponseUrl(IMG_URL) + "\" />";
+            final String SECURE_HTML = "<body>" + IMG_HTML + " " + JS_HTML + "</body>";
+            httpServer.setResponse(JS_URL, "window.loaded_js = 42;", null);
+            httpServer.setResponseBase64(IMG_URL,
+                    "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
+                    null);
+            String secureUrl = httpsServer.setResponse(SECURE_URL, SECURE_HTML, null);
+
+            mOnUiThread.clearSslPreferences();
+
+            mOnUiThread.setWebViewClient(new SslWebViewClient());
+
+            mSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW);
+            mOnUiThread.loadUrlAndWaitForCompletion(secureUrl);
+            assertEquals(1, httpsServer.getRequestCount(SECURE_URL));
+            assertEquals(0, httpServer.getRequestCount(JS_URL));
+            assertEquals(0, httpServer.getRequestCount(IMG_URL));
+
+            mSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
+            mOnUiThread.loadUrlAndWaitForCompletion(secureUrl);
+            assertEquals(2, httpsServer.getRequestCount(SECURE_URL));
+            assertEquals(1, httpServer.getRequestCount(JS_URL));
+            assertEquals(1, httpServer.getRequestCount(IMG_URL));
+
+            mSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
+            mOnUiThread.loadUrlAndWaitForCompletion(secureUrl);
+            assertEquals(3, httpsServer.getRequestCount(SECURE_URL));
+            assertEquals(1, httpServer.getRequestCount(JS_URL));
+            assertEquals(2, httpServer.getRequestCount(IMG_URL));
+        } finally {
+            if (httpServer != null) {
+                httpServer.shutdown();
+            }
+            if (httpsServer != null) {
+                httpsServer.shutdown();
+            }
+        }
+    }
+
     /**
      * Starts the internal web server. The server will be shut down automatically
      * during tearDown().