Add CTS tests for cleartext network traffic security policy.

Because the policy is set in apps' manifest, this CTS test needs to
use three different apps:
1. app which declares android:usesCleartextTraffic="true",
2. app which declares android:usesCleartextTraffic="false",
3. app which does not declare the android:usesCleartextTraffic
   attribute.

Moreover, because the app which a CTS test module instruments is
hard-coded in the module, we need three CTS test modules, one for each
of the above three apps.

As a result of this complication the relatively simple CTS tests are
split over multiple directories with most of the code shared between
them.

This CL also modifies CtsTestServer, the HTTP(S) server used in many
CTS tests, to not use HttpURLConnection to shut the server down.
CtsTestServer cliens shut it down by invoking CtsTestServer.shutdown
which makes a particular HTTP(S) request to the server which the
server then carries out. If HTTP traffic is blocked in an app, this
method of notifying the server will not work. To avoid entirely
changing the shutdown mechanism, this CL switching to issuing the
shutdown HTTP request using Socket API instead of the
HttpURLConnection API. This is because Socket API is not affected by
the cleartext network traffic policy.

Bug: 19215516
Change-Id: Id3c54bcf0e610b04884fba24707b81eb87917795
diff --git a/libs/testserver/src/android/webkit/cts/CtsTestServer.java b/libs/testserver/src/android/webkit/cts/CtsTestServer.java
index e39e435..de8a30d 100644
--- a/libs/testserver/src/android/webkit/cts/CtsTestServer.java
+++ b/libs/testserver/src/android/webkit/cts/CtsTestServer.java
@@ -229,12 +229,30 @@
             // 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();
+            if (url.getProtocol().equalsIgnoreCase("http")) {
+                // Use Socket instead of HttpURLConnection when the server is in cleartext HTTP mode
+                // to avoid the request being blocked by NetworkSecurityPolicy.
+                Socket socket = null;
+                try {
+                    socket = new Socket(url.getHost(), url.getPort());
+                    socket.getOutputStream().write(
+                        ("GET " + SHUTDOWN_PREFIX + " HTTP/1.0\r\n\r\n").getBytes("US-ASCII"));
+                    socket.getOutputStream().flush();
+                } finally {
+                    if (socket != null) {
+                        try {
+                            socket.close();
+                        } catch (Exception ignored) {}
+                    }
+                }
+            } else {
+                URLConnection connection = openConnection(url);
+                connection.connect();
 
-            // Read the input from the stream to send the request.
-            InputStream is = connection.getInputStream();
-            is.close();
+                // 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();
diff --git a/tests/netsecpolicy/Android.mk b/tests/netsecpolicy/Android.mk
new file mode 100644
index 0000000..137672e
--- /dev/null
+++ b/tests/netsecpolicy/Android.mk
@@ -0,0 +1,17 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/netsecpolicy/usescleartexttraffic-false/Android.mk b/tests/netsecpolicy/usescleartexttraffic-false/Android.mk
new file mode 100644
index 0000000..1af3b49
--- /dev/null
+++ b/tests/netsecpolicy/usescleartexttraffic-false/Android.mk
@@ -0,0 +1,30 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, \
+    ../usescleartexttraffic-shared/src)
+
+LOCAL_PACKAGE_NAME := CtsNetSecPolicyUsesCleartextTrafficFalse
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml b/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml
new file mode 100644
index 0000000..013821e
--- /dev/null
+++ b/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?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.netsecpolicy.usescleartext.false.cts">
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <application android:usesCleartextTraffic="false">
+    </application>
+</manifest>
diff --git a/tests/netsecpolicy/usescleartexttraffic-shared/src/Dummy.java b/tests/netsecpolicy/usescleartexttraffic-shared/src/Dummy.java
new file mode 100644
index 0000000..2705a5f
--- /dev/null
+++ b/tests/netsecpolicy/usescleartexttraffic-shared/src/Dummy.java
@@ -0,0 +1,2 @@
+public class Dummy {
+}
diff --git a/tests/netsecpolicy/usescleartexttraffic-true/Android.mk b/tests/netsecpolicy/usescleartexttraffic-true/Android.mk
new file mode 100644
index 0000000..5effc57
--- /dev/null
+++ b/tests/netsecpolicy/usescleartexttraffic-true/Android.mk
@@ -0,0 +1,30 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, \
+    ../usescleartexttraffic-shared/src)
+
+LOCAL_PACKAGE_NAME := CtsNetSecPolicyUsesCleartextTrafficTrue
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml b/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml
new file mode 100644
index 0000000..f50295e
--- /dev/null
+++ b/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?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.netsecpolicy.usescleartext.true.cts">
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-library android:name="org.apache.http.legacy"/>
+
+    <application android:usesCleartextTraffic="true">
+    </application>
+</manifest>
diff --git a/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk b/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk
new file mode 100644
index 0000000..685a16f
--- /dev/null
+++ b/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk
@@ -0,0 +1,30 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, \
+    ../usescleartexttraffic-shared/src)
+
+LOCAL_PACKAGE_NAME := CtsNetSecPolicyUsesCleartextTrafficUnspecified
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml b/tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml
new file mode 100644
index 0000000..7e735c7
--- /dev/null
+++ b/tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?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.netsecpolicy.usescleartext.unspecified.cts">
+     <uses-permission android:name="android.permission.INTERNET"/>
+    <application>
+    </application>
+</manifest>
diff --git a/tests/tests/netsecpolicy/Android.mk b/tests/tests/netsecpolicy/Android.mk
new file mode 100644
index 0000000..137672e
--- /dev/null
+++ b/tests/tests/netsecpolicy/Android.mk
@@ -0,0 +1,17 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/netsecpolicy/src/android/security/NetworkSecurityPolicyTestBase.java b/tests/tests/netsecpolicy/src/android/security/NetworkSecurityPolicyTestBase.java
new file mode 100644
index 0000000..0ab07ae
--- /dev/null
+++ b/tests/tests/netsecpolicy/src/android/security/NetworkSecurityPolicyTestBase.java
@@ -0,0 +1,380 @@
+package android.security;
+
+import android.app.DownloadManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.database.Cursor;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.net.http.AndroidHttpClient;
+import android.test.AndroidTestCase;
+import android.webkit.cts.CtsTestServer;
+
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.UnknownServiceException;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+abstract class NetworkSecurityPolicyTestBase extends AndroidTestCase {
+    private CtsTestServer mHttpOnlyWebServer;
+
+    private final boolean mCleartextTrafficExpectedToBePermitted;
+
+    NetworkSecurityPolicyTestBase(boolean cleartextTrafficExpectedToBePermitted) {
+        mCleartextTrafficExpectedToBePermitted = cleartextTrafficExpectedToBePermitted;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mHttpOnlyWebServer = new CtsTestServer(mContext, false);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        try {
+            mHttpOnlyWebServer.shutdown();
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    public void testNetworkSecurityPolicy() {
+        assertEquals(mCleartextTrafficExpectedToBePermitted,
+                NetworkSecurityPolicy.getInstance().isCleartextTrafficPermitted());
+    }
+
+    public void testApplicationInfoFlag() {
+        ApplicationInfo appInfo = getContext().getApplicationInfo();
+        int expectedValue = (mCleartextTrafficExpectedToBePermitted)
+                ? ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC : 0;
+        assertEquals(expectedValue, appInfo.flags & ApplicationInfo.FLAG_USES_CLEARTEXT_TRAFFIC);
+    }
+
+    public void testDefaultHttpURLConnection() throws Exception {
+        if (mCleartextTrafficExpectedToBePermitted) {
+            assertCleartextHttpURLConnectionSucceeds();
+        } else {
+            assertCleartextHttpURLConnectionBlocked();
+        }
+    }
+
+    private void assertCleartextHttpURLConnectionSucceeds() throws Exception {
+        URL url = new URL(mHttpOnlyWebServer.getUserAgentUrl());
+        HttpURLConnection conn = null;
+        try {
+            mHttpOnlyWebServer.resetRequestState();
+            conn = (HttpURLConnection) url.openConnection();
+            conn.setConnectTimeout(5000);
+            conn.setReadTimeout(5000);
+            assertEquals(200, conn.getResponseCode());
+        } finally {
+            if (conn != null) {
+                conn.disconnect();
+            }
+        }
+        Uri uri = Uri.parse(url.toString()).buildUpon().scheme(null).authority(null).build();
+        assertTrue(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
+    }
+
+    private void assertCleartextHttpURLConnectionBlocked() throws Exception {
+        URL url = new URL(mHttpOnlyWebServer.getUserAgentUrl());
+        HttpURLConnection conn = null;
+        try {
+            mHttpOnlyWebServer.resetRequestState();
+            conn = (HttpURLConnection) url.openConnection();
+            conn.setConnectTimeout(5000);
+            conn.setReadTimeout(5000);
+            conn.getResponseCode();
+            fail();
+        } catch (UnknownServiceException e) {
+            if ((e.getMessage() == null) || (!e.getMessage().toLowerCase().contains("cleartext"))) {
+                fail("Exception with which request failed does not mention cleartext: " + e);
+            }
+        } finally {
+            if (conn != null) {
+                conn.disconnect();
+            }
+        }
+        Uri uri = Uri.parse(url.toString()).buildUpon().scheme(null).authority(null).build();
+        assertFalse(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
+    }
+
+    public void testAndroidHttpClient() throws Exception {
+        if (mCleartextTrafficExpectedToBePermitted) {
+            assertAndroidHttpClientCleartextRequestSucceeds();
+        } else {
+            assertAndroidHttpClientCleartextRequestBlocked();
+        }
+    }
+
+    private void assertAndroidHttpClientCleartextRequestSucceeds() throws Exception {
+        URL url = new URL(mHttpOnlyWebServer.getUserAgentUrl());
+        AndroidHttpClient httpClient = AndroidHttpClient.newInstance(null);
+        try {
+            HttpResponse response = httpClient.execute(new HttpGet(url.toString()));
+            assertEquals(200, response.getStatusLine().getStatusCode());
+        } finally {
+            httpClient.close();
+        }
+        Uri uri = Uri.parse(url.toString()).buildUpon().scheme(null).authority(null).build();
+        assertTrue(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
+    }
+
+    private void assertAndroidHttpClientCleartextRequestBlocked() throws Exception {
+        URL url = new URL(mHttpOnlyWebServer.getUserAgentUrl());
+        AndroidHttpClient httpClient = AndroidHttpClient.newInstance(null);
+        try {
+            HttpResponse response = httpClient.execute(new HttpGet(url.toString()));
+            fail();
+        } catch (IOException e) {
+            if ((e.getMessage() == null) || (!e.getMessage().toLowerCase().contains("cleartext"))) {
+                fail("Exception with which request failed does not mention cleartext: " + e);
+            }
+        } finally {
+            httpClient.close();
+        }
+        Uri uri = Uri.parse(url.toString()).buildUpon().scheme(null).authority(null).build();
+        assertFalse(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
+    }
+
+    public void testMediaPlayer() throws Exception {
+        if (mCleartextTrafficExpectedToBePermitted) {
+            assertMediaPlayerCleartextRequestSucceeds();
+        } else {
+            assertMediaPlayerCleartextRequestBlocked();
+        }
+    }
+
+    private void assertMediaPlayerCleartextRequestSucceeds() throws Exception {
+        MediaPlayer mediaPlayer = new MediaPlayer();
+        Uri uri = Uri.parse(mHttpOnlyWebServer.getUserAgentUrl());
+        mediaPlayer.setDataSource(getContext(), uri);
+
+        try {
+            mediaPlayer.prepare();
+        } catch (IOException expected) {
+        } finally {
+            try {
+                mediaPlayer.stop();
+            } catch (IllegalStateException ignored) {
+            }
+        }
+        uri = uri.buildUpon().scheme(null).authority(null).build();
+        assertTrue(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
+    }
+
+    private void assertMediaPlayerCleartextRequestBlocked() throws Exception {
+        MediaPlayer mediaPlayer = new MediaPlayer();
+        Uri uri = Uri.parse(mHttpOnlyWebServer.getUserAgentUrl());
+        mediaPlayer.setDataSource(getContext(), uri);
+
+        try {
+            mediaPlayer.prepare();
+        } catch (IOException expected) {
+        } finally {
+            try {
+                mediaPlayer.stop();
+            } catch (IllegalStateException ignored) {
+            }
+        }
+        uri = uri.buildUpon().scheme(null).authority(null).build();
+        assertFalse(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
+    }
+
+    public void testDownloadManager() throws Exception {
+        Uri uri = Uri.parse(mHttpOnlyWebServer.getTestDownloadUrl("netsecpolicy", 0));
+        int[] result = downloadUsingDownloadManager(uri);
+        int status = result[0];
+        int reason = result[1];
+        uri = uri.buildUpon().scheme(null).authority(null).build();
+        if (mCleartextTrafficExpectedToBePermitted) {
+            assertEquals(DownloadManager.STATUS_SUCCESSFUL, status);
+            assertTrue(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
+        } else {
+            assertEquals(DownloadManager.STATUS_FAILED, status);
+            assertEquals(400, reason);
+            assertFalse(mHttpOnlyWebServer.wasResourceRequested(uri.toString()));
+        }
+    }
+
+
+    private int[] downloadUsingDownloadManager(Uri uri) throws Exception {
+        DownloadManager downloadManager =
+                (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE);
+        removeAllDownloads(downloadManager);
+        BroadcastReceiver downloadCompleteReceiver = null;
+        try {
+            final SettableFuture<Intent> downloadCompleteIntentFuture = new SettableFuture<Intent>();
+            downloadCompleteReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    downloadCompleteIntentFuture.set(intent);
+                }
+            };
+            getContext().registerReceiver(
+                    downloadCompleteReceiver,
+                    new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
+
+            Intent downloadCompleteIntent;
+
+            long downloadId = downloadManager.enqueue(new DownloadManager.Request(uri));
+            downloadCompleteIntent = downloadCompleteIntentFuture.get(5, TimeUnit.SECONDS);
+
+            assertEquals(downloadId,
+                    downloadCompleteIntent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1));
+            Cursor c = downloadManager.query(
+                    new DownloadManager.Query().setFilterById(downloadId));
+            try {
+                if (!c.moveToNext()) {
+                    fail("Download not found");
+                    return null;
+                }
+                int status = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
+                int reason = c.getInt(c.getColumnIndexOrThrow(DownloadManager.COLUMN_REASON));
+                return new int[] {status, reason};
+            } finally {
+                c.close();
+            }
+        } finally {
+            if (downloadCompleteReceiver != null) {
+                getContext().unregisterReceiver(downloadCompleteReceiver);
+            }
+            removeAllDownloads(downloadManager);
+        }
+    }
+
+    private static void removeAllDownloads(DownloadManager downloadManager) {
+        Cursor cursor = null;
+        try {
+            DownloadManager.Query query = new DownloadManager.Query();
+            cursor = downloadManager.query(query);
+            if (cursor.getCount() == 0) {
+                return;
+            }
+            long[] removeIds = new long[cursor.getCount()];
+            int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_ID);
+            for (int i = 0; cursor.moveToNext(); i++) {
+                removeIds[i] = cursor.getLong(columnIndex);
+            }
+            assertEquals(removeIds.length, downloadManager.remove(removeIds));
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    private static class SettableFuture<T> implements Future<T> {
+
+        private final Object mLock = new Object();
+        private boolean mDone;
+        private boolean mCancelled;
+        private T mValue;
+        private Throwable mException;
+
+        public void set(T value) {
+            synchronized (mLock) {
+                if (!mDone) {
+                    mValue = value;
+                    mDone = true;
+                    mLock.notifyAll();
+                }
+            }
+        }
+
+        public void setException(Throwable exception) {
+            synchronized (mLock) {
+                if (!mDone) {
+                    mException = exception;
+                    mDone = true;
+                    mLock.notifyAll();
+                }
+            }
+        }
+
+        @Override
+        public boolean cancel(boolean mayInterruptIfRunning) {
+            synchronized (mLock) {
+                if (mDone) {
+                    return false;
+                }
+                mCancelled = true;
+                mDone = true;
+                mLock.notifyAll();
+                return true;
+            }
+        }
+
+        @Override
+        public T get() throws InterruptedException, ExecutionException {
+            synchronized (mLock) {
+                while (!mDone) {
+                    mLock.wait();
+                }
+                return getValue();
+            }
+        }
+
+        @Override
+        public T get(long timeout, TimeUnit timeUnit)
+                throws InterruptedException, ExecutionException, TimeoutException {
+            synchronized (mLock) {
+                if (mDone) {
+                    return getValue();
+                }
+                long timeoutMillis = timeUnit.toMillis(timeout);
+                long deadlineTimeMillis = System.currentTimeMillis() + timeoutMillis;
+
+                while (!mDone) {
+                    long millisTillDeadline = deadlineTimeMillis - System.currentTimeMillis();
+                    if ((millisTillDeadline <= 0) || (millisTillDeadline > timeoutMillis)) {
+                        throw new TimeoutException();
+                    }
+                    mLock.wait(millisTillDeadline);
+                }
+                return getValue();
+            }
+        }
+
+        private T getValue() throws ExecutionException {
+            synchronized (mLock) {
+                if (!mDone) {
+                    throw new IllegalStateException("Not yet done");
+                }
+                if (mCancelled) {
+                    throw new CancellationException();
+                }
+                if (mException != null) {
+                    throw new ExecutionException(mException);
+                }
+                return mValue;
+            }
+        }
+
+        @Override
+        public boolean isCancelled() {
+            synchronized (mLock) {
+                return mCancelled;
+            }
+        }
+
+        @Override
+        public boolean isDone() {
+            synchronized (mLock) {
+                return mDone;
+            }
+        }
+    }
+}
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-false/Android.mk b/tests/tests/netsecpolicy/usescleartexttraffic-false/Android.mk
new file mode 100644
index 0000000..0441f2b
--- /dev/null
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-false/Android.mk
@@ -0,0 +1,35 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner \
+    ctstestserver \
+    org.apache.http.legacy
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src ../src)
+
+LOCAL_PACKAGE_NAME := CtsNetSecPolicyUsesCleartextTrafficFalseTestCases
+
+LOCAL_INSTRUMENTATION_FOR := CtsNetSecPolicyUsesCleartextTrafficFalse
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml b/tests/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml
new file mode 100644
index 0000000..49385f8
--- /dev/null
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-false/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?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="com.android.cts.netsecpolicy.usescleartext.false">
+
+  <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+  <application>
+      <uses-library android:name="android.test.runner"/>
+  </application>
+
+  <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                   android:targetPackage="android.netsecpolicy.usescleartext.false.cts"
+                   android:label="Tests for NetworkSecurityPolicy cleartext traffic policy when it is set to denied.">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-false/src/android/security/NetworkSecurityPolicyCleartextDeniedTest.java b/tests/tests/netsecpolicy/usescleartexttraffic-false/src/android/security/NetworkSecurityPolicyCleartextDeniedTest.java
new file mode 100644
index 0000000..f5d9770
--- /dev/null
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-false/src/android/security/NetworkSecurityPolicyCleartextDeniedTest.java
@@ -0,0 +1,9 @@
+package android.security;
+
+public class NetworkSecurityPolicyCleartextDeniedTest extends NetworkSecurityPolicyTestBase {
+
+    public NetworkSecurityPolicyCleartextDeniedTest() {
+        super(false // expect cleartext traffic to be blocked
+                );
+    }
+}
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-true/Android.mk b/tests/tests/netsecpolicy/usescleartexttraffic-true/Android.mk
new file mode 100644
index 0000000..5a4a41d
--- /dev/null
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-true/Android.mk
@@ -0,0 +1,35 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner \
+    ctstestserver \
+    org.apache.http.legacy
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src ../src)
+
+LOCAL_PACKAGE_NAME := CtsNetSecPolicyUsesCleartextTrafficTrueTestCases
+
+LOCAL_INSTRUMENTATION_FOR := CtsNetSecPolicyUsesCleartextTrafficTrue
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml b/tests/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml
new file mode 100644
index 0000000..be698f2
--- /dev/null
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-true/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?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="com.android.cts.netsecpolicy.usescleartext.true">
+
+  <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+  <application>
+      <uses-library android:name="android.test.runner"/>
+  </application>
+
+  <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                   android:targetPackage="android.netsecpolicy.usescleartext.true.cts"
+                   android:label="Tests for NetworkSecurityPolicy cleartext traffic policy when it is set to permitted.">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-true/src/android/security/NetworkSecurityPolicyCleartextPermittedTest.java b/tests/tests/netsecpolicy/usescleartexttraffic-true/src/android/security/NetworkSecurityPolicyCleartextPermittedTest.java
new file mode 100644
index 0000000..83c1049
--- /dev/null
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-true/src/android/security/NetworkSecurityPolicyCleartextPermittedTest.java
@@ -0,0 +1,9 @@
+package android.security;
+
+public class NetworkSecurityPolicyCleartextPermittedTest extends NetworkSecurityPolicyTestBase {
+
+    public NetworkSecurityPolicyCleartextPermittedTest() {
+        super(true // expect cleartext traffic to be permitted
+                );
+    }
+}
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk
new file mode 100644
index 0000000..faa3c23
--- /dev/null
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/Android.mk
@@ -0,0 +1,35 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner \
+    ctstestserver \
+    org.apache.http.legacy
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src ../src)
+
+LOCAL_PACKAGE_NAME := CtsNetSecPolicyUsesCleartextTrafficUnspecifiedTestCases
+
+LOCAL_INSTRUMENTATION_FOR := CtsNetSecPolicyUsesCleartextTrafficUnspecified
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml
new file mode 100644
index 0000000..7bd8742
--- /dev/null
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?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="com.android.cts.netsecpolicy.usescleartext.unspecified">
+
+  <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+  <application>
+      <uses-library android:name="android.test.runner"/>
+  </application>
+
+  <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                   android:targetPackage="android.netsecpolicy.usescleartext.unspecified.cts"
+                   android:label="Tests for NetworkSecurityPolicy cleartext traffic policy when it is not specified.">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
diff --git a/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/src/android/security/NetworkSecurityPolicyCleartextUnspecifiedTest.java b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/src/android/security/NetworkSecurityPolicyCleartextUnspecifiedTest.java
new file mode 100644
index 0000000..5690f31
--- /dev/null
+++ b/tests/tests/netsecpolicy/usescleartexttraffic-unspecified/src/android/security/NetworkSecurityPolicyCleartextUnspecifiedTest.java
@@ -0,0 +1,9 @@
+package android.security;
+
+public class NetworkSecurityPolicyCleartextUnspecifiedTest extends NetworkSecurityPolicyTestBase {
+
+    public NetworkSecurityPolicyCleartextUnspecifiedTest() {
+        super(true // expect cleartext traffic to be permitted
+                );
+    }
+}