blob: f55af35ac58eb23c461d9901edc2819a4305df72 [file] [log] [blame]
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 (IOException 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;
}
}
}
}