blob: fecc067ac28f84e3df152e47bd122c11511df189 [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os.cts;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.TrafficStats;
import android.net.Uri;
import android.os.StrictMode;
import android.os.SystemClock;
import android.system.Os;
import android.system.OsConstants;
import android.test.InstrumentationTestCase;
import android.util.Log;
import libcore.io.Streams;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Tests for {@link StrictMode}
*/
public class StrictModeTest extends InstrumentationTestCase {
private static final String TAG = "StrictModeTest";
private StrictMode.ThreadPolicy mThreadPolicy;
private StrictMode.VmPolicy mVmPolicy;
private Context getContext() {
return getInstrumentation().getContext();
}
@Override
protected void setUp() {
mThreadPolicy = StrictMode.getThreadPolicy();
mVmPolicy = StrictMode.getVmPolicy();
}
@Override
protected void tearDown() {
StrictMode.setThreadPolicy(mThreadPolicy);
StrictMode.setVmPolicy(mVmPolicy);
}
/**
* Insecure connection should be detected
*/
public void testCleartextNetwork() throws Exception {
if (!hasInternetConnection()) {
Log.i(TAG, "testCleartextNetwork() ignored on device without Internet");
return;
}
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectCleartextNetwork()
.penaltyLog()
.build());
long mark = System.currentTimeMillis();
((HttpURLConnection) new URL("http://example.com/").openConnection()).getResponseCode();
assertLogged("Detected cleartext network traffic from UID "
+ android.os.Process.myUid(), mark, 5000);
}
/**
* Secure connection should be ignored
*/
public void testEncryptedNetwork() throws Exception {
if (!hasInternetConnection()) {
Log.i(TAG, "testEncryptedNetwork() ignored on device without Internet");
return;
}
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectCleartextNetwork()
.penaltyLog()
.build());
long mark = System.currentTimeMillis();
((HttpURLConnection) new URL("https://example.com/").openConnection()).getResponseCode();
assertNotLogged("Detected cleartext network traffic from UID "
+ android.os.Process.myUid(), mark, 5000);
}
public void testFileUriExposure() throws Exception {
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectFileUriExposure()
.penaltyLog()
.build());
long mark = System.currentTimeMillis();
Uri uri = Uri.fromFile(new File("/sdcard/meow.jpg"));
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "image/jpeg");
getContext().startActivity(intent);
assertLogged(uri + " exposed beyond app", mark);
mark = System.currentTimeMillis();
uri = Uri.parse("content://com.example/foobar");
intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "image/jpeg");
getContext().startActivity(intent);
assertNotLogged(uri + " exposed beyond app", mark);
}
public void testContentUriWithoutPermission() throws Exception {
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectContentUriWithoutPermission()
.penaltyLog()
.build());
long mark = System.currentTimeMillis();
final Uri uri = Uri.parse("content://com.example/foobar");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "image/jpeg");
getContext().startActivity(intent);
assertLogged(uri + " exposed beyond app", mark);
mark = System.currentTimeMillis();
intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "image/jpeg");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
getContext().startActivity(intent);
assertNotLogged(uri + " exposed beyond app", mark);
}
public void testUntaggedSocketsHttp() throws Exception {
if (!hasInternetConnection()) {
Log.i(TAG, "testUntaggedSockets() ignored on device without Internet");
return;
}
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectUntaggedSockets()
.penaltyLog()
.build());
long mark = System.currentTimeMillis();
((HttpURLConnection) new URL("http://example.com/").openConnection()).getResponseCode();
assertLogged("Untagged socket detected", mark);
mark = System.currentTimeMillis();
TrafficStats.setThreadStatsTag(0xDECAFBAD);
try {
((HttpURLConnection) new URL("http://example.com/").openConnection()).getResponseCode();
} finally {
TrafficStats.clearThreadStatsTag();
}
assertNotLogged("Untagged socket detected", mark);
}
public void testUntaggedSocketsRaw() throws Exception {
if (!hasInternetConnection()) {
Log.i(TAG, "testUntaggedSockets() ignored on device without Internet");
return;
}
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectUntaggedSockets()
.penaltyLog()
.build());
long mark = System.currentTimeMillis();
TrafficStats.setThreadStatsTag(0xDECAFBAD);
try (Socket socket = new Socket("example.com", 80)) {
socket.getOutputStream().close();
} finally {
TrafficStats.clearThreadStatsTag();
}
assertNotLogged("Untagged socket detected", mark);
mark = System.currentTimeMillis();
try (Socket socket = new Socket("example.com", 80)) {
socket.getOutputStream().close();
}
assertLogged("Untagged socket detected", mark);
}
public void testRead() throws Exception {
final File test = File.createTempFile("foo", "bar");
final File dir = test.getParentFile();
FileInputStream is = null;
FileDescriptor fd = null;
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.penaltyLog()
.build());
SystemClock.sleep(1500);
try (AssertDiskReadLogged l = new AssertDiskReadLogged()) {
test.exists();
}
try (AssertDiskReadLogged l = new AssertDiskReadLogged()) {
test.length();
}
try (AssertDiskReadLogged l = new AssertDiskReadLogged()) {
dir.list();
}
try (AssertDiskReadLogged l = new AssertDiskReadLogged()) {
is = new FileInputStream(test);
}
try (AssertDiskReadLogged l = new AssertDiskReadLogged()) {
is.read();
}
try (AssertDiskReadLogged l = new AssertDiskReadLogged()) {
fd = Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, 0600);
}
try (AssertDiskReadLogged l = new AssertDiskReadLogged()) {
Os.read(fd, new byte[10], 0, 1);
}
}
public void testWrite() throws Exception {
File file = null;
FileOutputStream os = null;
FileDescriptor fd = null;
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskWrites()
.penaltyLog()
.build());
SystemClock.sleep(1500);
try (AssertDiskWriteLogged l = new AssertDiskWriteLogged()) {
file = File.createTempFile("foo", "bar");
}
try (AssertDiskWriteLogged l = new AssertDiskWriteLogged()) {
file.delete();
}
try (AssertDiskWriteLogged l = new AssertDiskWriteLogged()) {
file.createNewFile();
}
try (AssertDiskWriteLogged l = new AssertDiskWriteLogged()) {
os = new FileOutputStream(file);
}
try (AssertDiskWriteLogged l = new AssertDiskWriteLogged()) {
os.write(32);
}
try (AssertDiskWriteLogged l = new AssertDiskWriteLogged()) {
fd = Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, 0600);
}
try (AssertDiskWriteLogged l = new AssertDiskWriteLogged()) {
Os.write(fd, new byte[10], 0, 1);
}
try (AssertDiskWriteLogged l = new AssertDiskWriteLogged()) {
Os.fsync(fd);
}
try (AssertDiskWriteLogged l = new AssertDiskWriteLogged()) {
file.renameTo(new File(file.getParent(), "foobar"));
}
}
public void testNetwork() throws Exception {
if (!hasInternetConnection()) {
Log.i(TAG, "testUntaggedSockets() ignored on device without Internet");
return;
}
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectNetwork()
.penaltyLog()
.build());
long mark = System.currentTimeMillis();
try (Socket socket = new Socket("example.com", 80)) {
socket.getOutputStream().close();
}
assertLogged("StrictModeNetworkViolation", mark);
mark = System.currentTimeMillis();
((HttpURLConnection) new URL("http://example.com/").openConnection()).getResponseCode();
assertLogged("StrictModeNetworkViolation", mark);
}
private static class AssertLogged implements AutoCloseable {
private final String mMessage;
private final long mStart;
public AssertLogged(String message) {
mMessage = message;
mStart = System.currentTimeMillis();
}
@Override
public void close() throws Exception {
assertLogged(mMessage, mStart);
}
}
private static class AssertDiskReadLogged extends AssertLogged {
public AssertDiskReadLogged() {
super("StrictModeDiskReadViolation");
}
}
private static class AssertDiskWriteLogged extends AssertLogged {
public AssertDiskWriteLogged() {
super("StrictModeDiskWriteViolation");
}
}
private static void assertLogged(String msg, long since) throws Exception {
assertLogged(msg, since, 1100);
}
private static void assertLogged(String msg, long since, long wait) throws Exception {
SystemClock.sleep(wait);
assertTrue("Expected message not found: " + msg, readLogSince(since).contains(msg));
}
private static void assertNotLogged(String msg, long since) throws Exception {
assertNotLogged(msg, since, 1100);
}
private static void assertNotLogged(String msg, long since, long wait) throws Exception {
SystemClock.sleep(wait);
assertFalse("Unexpected message found: " + msg, readLogSince(since).contains(msg));
}
private static String readLogSince(long millis) throws Exception {
final SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
final Process proc = new ProcessBuilder("logcat", "-t", format.format(new Date(millis)))
.redirectErrorStream(true).start();
final ByteArrayOutputStream buf = new ByteArrayOutputStream();
Streams.copy(proc.getInputStream(), buf);
final int res = proc.waitFor();
Log.d(TAG, "Log output was " + buf.size() + " bytes, exit code " + res);
return new String(buf.toByteArray());
}
private boolean hasInternetConnection() {
final PackageManager pm = getContext().getPackageManager();
return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
|| pm.hasSystemFeature(PackageManager.FEATURE_WIFI)
|| pm.hasSystemFeature(PackageManager.FEATURE_ETHERNET);
}
}