/* * 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 list = new ArrayList(); 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 checkResourceMismatch = (CheckBox) findViewById(R.id.policy_resource_mismatch); 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 (checkResourceMismatch.isChecked()) newPolicy.detectResourceMismatches(); 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); checkResourceMismatch.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; } }