blob: b68c0fa0f611f823e6d4b2c63ed837b4a6ca3782 [file] [log] [blame]
/*
* Copyright (C) 2010 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 com.android.strictmodetest;
import android.app.Activity;
import android.content.ComponentName;
import android.content.ContentQueryMap;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.IContentProvider;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.net.Uri;
import android.os.Bundle;
import android.os.Debug;
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemClock;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.AndroidException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
import dalvik.system.BlockGuard;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.InetAddress;
import java.net.Socket;
import java.net.URL;
import java.util.ArrayList;
public class StrictModeActivity extends Activity {
private static final String TAG = "StrictModeActivity";
private static final Uri SYSTEM_SETTINGS_URI = Uri.parse("content://settings/system");
private ContentResolver cr;
private final static class SimpleConnection implements ServiceConnection {
public IService stub = null;
public void onServiceConnected(ComponentName name, IBinder service) {
stub = IService.Stub.asInterface(service);
Log.v(TAG, "Service connected: " + name);
}
public void onServiceDisconnected(ComponentName name) {
stub = null;
Log.v(TAG, "Service disconnected: " + name);
}
}
private final SimpleConnection mLocalServiceConn = new SimpleConnection();
private final SimpleConnection mRemoteServiceConn = new SimpleConnection();
private SQLiteDatabase mDb;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
cr = getContentResolver();
mDb = openOrCreateDatabase("foo.db", MODE_PRIVATE, null);
final Button readButton = (Button) findViewById(R.id.read_button);
readButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
SharedPreferences prefs = getSharedPreferences("foo", 0);
try {
Cursor c = null;
try {
c = mDb.rawQuery("SELECT * FROM foo", null);
} finally {
if (c != null) c.close();
}
} catch (android.database.sqlite.SQLiteException e) {
Log.e(TAG, "SQLiteException: " + e);
}
}
});
final Button writeButton = (Button) findViewById(R.id.write_button);
writeButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mDb.execSQL("CREATE TABLE IF NOT EXISTS FOO (a INT)");
SharedPreferences prefs = getSharedPreferences("foo", 0);
prefs.edit().putLong("time", System.currentTimeMillis()).commit();
}
});
final Button writeLoopButton = (Button) findViewById(R.id.write_loop_button);
writeLoopButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
long startTime = SystemClock.uptimeMillis();
int iters = 1000;
BlockGuard.Policy policy = BlockGuard.getThreadPolicy();
for (int i = 0; i < iters; ++i) {
policy.onWriteToDisk();
}
long endTime = SystemClock.uptimeMillis();
Log.d(TAG, "Time for " + iters + ": " + (endTime - startTime) + ", avg=" +
(endTime - startTime) / (double) iters);
}
});
final Button dnsButton = (Button) findViewById(R.id.dns_button);
dnsButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Log.d(TAG, "Doing DNS lookup for www.l.google.com... "
+ "(may be cached by InetAddress)");
try {
InetAddress[] addrs = InetAddress.getAllByName("www.l.google.com");
for (int i = 0; i < addrs.length; ++i) {
Log.d(TAG, "got: " + addrs[i]);
}
} catch (java.net.UnknownHostException e) {
Log.d(TAG, "DNS error: " + e);
}
// Now try a random hostname to evade libcore's
// DNS caching.
try {
String random = "" + Math.random();
random = random.substring(random.indexOf(".") + 1);
String domain = random + ".livejournal.com";
InetAddress addr = InetAddress.getByName(domain);
Log.d(TAG, "for random domain " + domain + ": " + addr);
} catch (java.net.UnknownHostException e) {
}
}
});
final Button httpButton = (Button) findViewById(R.id.http_button);
httpButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
// Note: not using AndroidHttpClient, as that comes with its
// own pre-StrictMode network-on-Looper thread check. The
// intent of this test is that we test the network stack's
// instrumentation for StrictMode instead.
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpResponse res = httpClient.execute(
new HttpGet("http://www.android.com/favicon.ico"));
Log.d(TAG, "Fetched http response: " + res);
} catch (IOException e) {
Log.d(TAG, "HTTP fetch error: " + e);
}
}
});
final Button http2Button = (Button) findViewById(R.id.http2_button);
http2Button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
// Usually this ends up tripping in DNS resolution,
// so see http3Button below, which connects directly to an IP
InputStream is = new URL("http://www.android.com/")
.openConnection()
.getInputStream();
Log.d(TAG, "Got input stream: " + is);
} catch (IOException e) {
Log.d(TAG, "HTTP fetch error: " + e);
}
}
});
final Button http3Button = (Button) findViewById(R.id.http3_button);
http3Button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
// One of Google's web IPs, as of 2010-06-16....
InputStream is = new URL("http://74.125.19.14/")
.openConnection()
.getInputStream();
Log.d(TAG, "Got input stream: " + is);
} catch (IOException e) {
Log.d(TAG, "HTTP fetch error: " + e);
}
}
});
final Button binderLocalButton = (Button) findViewById(R.id.binder_local_button);
binderLocalButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
boolean value = mLocalServiceConn.stub.doDiskWrite(123 /* dummy */);
Log.d(TAG, "local writeToDisk returned: " + value);
} catch (RemoteException e) {
Log.d(TAG, "local binderButton error: " + e);
}
}
});
final Button binderRemoteButton = (Button) findViewById(R.id.binder_remote_button);
binderRemoteButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
boolean value = mRemoteServiceConn.stub.doDiskWrite(1);
Log.d(TAG, "remote writeToDisk #1 returned: " + value);
value = mRemoteServiceConn.stub.doDiskWrite(2);
Log.d(TAG, "remote writeToDisk #2 returned: " + value);
} catch (RemoteException e) {
Log.d(TAG, "remote binderButton error: " + e);
}
}
});
final Button binderOneWayButton = (Button) findViewById(R.id.binder_oneway_button);
binderOneWayButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
Log.d(TAG, "doing oneway disk write over Binder.");
mRemoteServiceConn.stub.doDiskOneWay();
} catch (RemoteException e) {
Log.d(TAG, "remote binderButton error: " + e);
}
}
});
final Button binderCheckButton = (Button) findViewById(R.id.binder_check_button);
binderCheckButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
int policy;
try {
policy = mLocalServiceConn.stub.getThreadPolicy();
Log.d(TAG, "local service policy: " + policy);
policy = mRemoteServiceConn.stub.getThreadPolicy();
Log.d(TAG, "remote service policy: " + policy);
} catch (RemoteException e) {
Log.d(TAG, "binderCheckButton error: " + e);
}
}
});
final Button serviceDumpButton = (Button) findViewById(R.id.service_dump);
serviceDumpButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Log.d(TAG, "About to do a service dump...");
File file = new File("/sdcard/strictmode-service-dump.txt");
FileOutputStream output = null;
final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
try {
StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.LAX);
output = new FileOutputStream(file);
StrictMode.setThreadPolicy(oldPolicy);
boolean dumped = Debug.dumpService("cpuinfo",
output.getFD(), new String[0]);
Log.d(TAG, "Dumped = " + dumped);
} catch (IOException e) {
Log.e(TAG, "Can't dump service", e);
} finally {
StrictMode.setThreadPolicy(oldPolicy);
}
Log.d(TAG, "Did service dump.");
}
});
final Button lingerCloseButton = (Button) findViewById(R.id.linger_close_button);
lingerCloseButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
closeWithLinger(true);
}
});
final Button nonlingerCloseButton = (Button) findViewById(R.id.nonlinger_close_button);
nonlingerCloseButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
closeWithLinger(false);
}
});
final Button leakCursorButton = (Button) findViewById(R.id.leak_cursor_button);
leakCursorButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
final StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy();
try {
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.penaltyLog()
.penaltyDropBox()
.build());
mDb.execSQL("CREATE TABLE IF NOT EXISTS FOO (a INT)");
Cursor c = mDb.rawQuery("SELECT * FROM foo", null);
c = null; // never close it
Runtime.getRuntime().gc();
} finally {
StrictMode.setVmPolicy(oldPolicy);
}
}
});
final Button customButton = (Button) findViewById(R.id.custom_button);
customButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
StrictMode.noteSlowCall("my example call");
}
});
final Button gcInstanceButton = (Button) findViewById(R.id.gc_instance_button);
gcInstanceButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
ArrayList<DummyObject> list = new ArrayList<DummyObject>();
list.add(new DummyObject());
list.add(new DummyObject());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder(StrictMode.getVmPolicy())
.setClassInstanceLimit(DummyObject.class, 1)
.penaltyLog()
.penaltyDropBox()
.build());
StrictMode.conditionallyCheckInstanceCounts();
list.clear();
}
});
final CheckBox checkNoWrite = (CheckBox) findViewById(R.id.policy_no_write);
final CheckBox checkNoRead = (CheckBox) findViewById(R.id.policy_no_reads);
final CheckBox checkNoNetwork = (CheckBox) findViewById(R.id.policy_no_network);
final CheckBox checkCustom = (CheckBox) findViewById(R.id.policy_custom);
final CheckBox checkPenaltyLog = (CheckBox) findViewById(R.id.policy_penalty_log);
final CheckBox checkPenaltyDialog = (CheckBox) findViewById(R.id.policy_penalty_dialog);
final CheckBox checkPenaltyDeath = (CheckBox) findViewById(R.id.policy_penalty_death);
final CheckBox checkPenaltyDropBox = (CheckBox) findViewById(R.id.policy_penalty_dropbox);
final CheckBox checkPenaltyFlash = (CheckBox) findViewById(R.id.policy_penalty_flash);
final CheckBox checkPenaltyNetworkDeath = (CheckBox) findViewById(R.id.policy_penalty_network_death);
View.OnClickListener changePolicy = new View.OnClickListener() {
public void onClick(View v) {
StrictMode.ThreadPolicy.Builder newPolicy = new StrictMode.ThreadPolicy.Builder();
if (checkNoWrite.isChecked()) newPolicy.detectDiskWrites();
if (checkNoRead.isChecked()) newPolicy.detectDiskReads();
if (checkNoNetwork.isChecked()) newPolicy.detectNetwork();
if (checkCustom.isChecked()) newPolicy.detectCustomSlowCalls();
if (checkPenaltyLog.isChecked()) newPolicy.penaltyLog();
if (checkPenaltyDialog.isChecked()) newPolicy.penaltyDialog();
if (checkPenaltyDeath.isChecked()) newPolicy.penaltyDeath();
if (checkPenaltyDropBox.isChecked()) newPolicy.penaltyDropBox();
if (checkPenaltyFlash.isChecked()) newPolicy.penaltyFlashScreen();
if (checkPenaltyNetworkDeath.isChecked()) newPolicy.penaltyDeathOnNetwork();
StrictMode.ThreadPolicy policy = newPolicy.build();
Log.v(TAG, "Changing policy to: " + policy);
StrictMode.setThreadPolicy(policy);
}
};
checkNoWrite.setOnClickListener(changePolicy);
checkNoRead.setOnClickListener(changePolicy);
checkNoNetwork.setOnClickListener(changePolicy);
checkCustom.setOnClickListener(changePolicy);
checkPenaltyLog.setOnClickListener(changePolicy);
checkPenaltyDialog.setOnClickListener(changePolicy);
checkPenaltyDeath.setOnClickListener(changePolicy);
checkPenaltyDropBox.setOnClickListener(changePolicy);
checkPenaltyFlash.setOnClickListener(changePolicy);
checkPenaltyNetworkDeath.setOnClickListener(changePolicy);
}
@Override
public void onDestroy() {
super.onDestroy();
mDb.close();
mDb = null;
}
private void closeWithLinger(boolean linger) {
Log.d(TAG, "Socket linger test; linger=" + linger);
try {
Socket socket = new Socket();
socket.setSoLinger(linger, 5);
socket.close();
} catch (IOException e) {
Log.e(TAG, "Error with linger close", e);
}
}
private void fileReadLoop() {
RandomAccessFile raf = null;
File filename = getFileStreamPath("test.dat");
try {
long sumNanos = 0;
byte[] buf = new byte[512];
//raf = new RandomAccessFile(filename, "rw");
//raf.write(buf);
//raf.close();
//raf = null;
// The data's almost certainly cached -- it's not clear what we're testing here
raf = new RandomAccessFile(filename, "r");
raf.seek(0);
raf.read(buf);
} catch (IOException e) {
Log.e(TAG, "File read failed", e);
} finally {
try { if (raf != null) raf.close(); } catch (IOException e) {}
}
}
// Returns milliseconds taken, or -1 on failure.
private long settingsWrite(int mode) {
Cursor c = null;
long startTime = SystemClock.uptimeMillis();
// The database will take care of replacing duplicates.
try {
ContentValues values = new ContentValues();
values.put("name", "dummy_for_testing");
values.put("value", "" + startTime);
Uri uri = cr.insert(SYSTEM_SETTINGS_URI, values);
Log.v(TAG, "inserted uri: " + uri);
} catch (SQLException e) {
Log.w(TAG, "sqliteexception during write: " + e);
return -1;
}
long duration = SystemClock.uptimeMillis() - startTime;
return duration;
}
@Override public void onResume() {
super.onResume();
bindService(new Intent(this, LocalService.class),
mLocalServiceConn, Context.BIND_AUTO_CREATE);
bindService(new Intent(this, RemoteService.class),
mRemoteServiceConn, Context.BIND_AUTO_CREATE);
}
@Override public void onPause() {
super.onPause();
unbindService(mLocalServiceConn);
unbindService(mRemoteServiceConn);
}
private static class DummyObject {
int foo;
}
}