StrictModeActivity.java revision 0aed0d20df4984c1df5145356929a904852ac4ec
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.strictmodetest; 18 19import android.app.Activity; 20import android.content.ComponentName; 21import android.content.ContentQueryMap; 22import android.content.ContentResolver; 23import android.content.ContentValues; 24import android.content.Context; 25import android.content.IContentProvider; 26import android.content.Intent; 27import android.content.SharedPreferences; 28import android.content.ServiceConnection; 29import android.content.pm.PackageManager; 30import android.content.pm.ResolveInfo; 31import android.content.res.Configuration; 32import android.content.res.Resources; 33import android.database.Cursor; 34import android.database.SQLException; 35import android.database.sqlite.SQLiteDatabase; 36import android.net.LocalSocket; 37import android.net.LocalSocketAddress; 38import android.net.Uri; 39import android.os.Bundle; 40import android.os.Debug; 41import android.os.Handler; 42import android.os.IBinder; 43import android.os.Parcel; 44import android.os.RemoteException; 45import android.os.ServiceManager; 46import android.os.StrictMode; 47import android.os.SystemClock; 48import android.telephony.TelephonyManager; 49import android.text.TextUtils; 50import android.util.AndroidException; 51import android.util.Config; 52import android.util.Log; 53import android.view.View; 54import android.widget.Button; 55import android.widget.CheckBox; 56import android.widget.TextView; 57 58import dalvik.system.BlockGuard; 59 60import org.apache.http.HttpResponse; 61import org.apache.http.client.methods.HttpUriRequest; 62import org.apache.http.client.methods.HttpGet; 63import org.apache.http.impl.client.DefaultHttpClient; 64 65import java.io.File; 66import java.io.FileInputStream; 67import java.io.FileOutputStream; 68import java.io.IOException; 69import java.io.InputStream; 70import java.io.OutputStream; 71import java.io.RandomAccessFile; 72import java.net.InetAddress; 73import java.net.Socket; 74import java.net.URL; 75import java.util.ArrayList; 76 77public class StrictModeActivity extends Activity { 78 79 private static final String TAG = "StrictModeActivity"; 80 private static final Uri SYSTEM_SETTINGS_URI = Uri.parse("content://settings/system"); 81 82 private ContentResolver cr; 83 84 private final static class SimpleConnection implements ServiceConnection { 85 public IService stub = null; 86 public void onServiceConnected(ComponentName name, IBinder service) { 87 stub = IService.Stub.asInterface(service); 88 Log.v(TAG, "Service connected: " + name); 89 } 90 public void onServiceDisconnected(ComponentName name) { 91 stub = null; 92 Log.v(TAG, "Service disconnected: " + name); 93 } 94 } 95 96 private final SimpleConnection mLocalServiceConn = new SimpleConnection(); 97 private final SimpleConnection mRemoteServiceConn = new SimpleConnection(); 98 99 private SQLiteDatabase mDb; 100 101 /** Called when the activity is first created. */ 102 @Override 103 public void onCreate(Bundle savedInstanceState) { 104 super.onCreate(savedInstanceState); 105 setContentView(R.layout.main); 106 107 cr = getContentResolver(); 108 mDb = openOrCreateDatabase("foo.db", MODE_PRIVATE, null); 109 110 final Button readButton = (Button) findViewById(R.id.read_button); 111 readButton.setOnClickListener(new View.OnClickListener() { 112 public void onClick(View v) { 113 SharedPreferences prefs = getSharedPreferences("foo", 0); 114 try { 115 Cursor c = null; 116 try { 117 c = mDb.rawQuery("SELECT * FROM foo", null); 118 } finally { 119 if (c != null) c.close(); 120 } 121 } catch (android.database.sqlite.SQLiteException e) { 122 Log.e(TAG, "SQLiteException: " + e); 123 } 124 } 125 }); 126 127 final Button writeButton = (Button) findViewById(R.id.write_button); 128 writeButton.setOnClickListener(new View.OnClickListener() { 129 public void onClick(View v) { 130 mDb.execSQL("CREATE TABLE IF NOT EXISTS FOO (a INT)"); 131 SharedPreferences prefs = getSharedPreferences("foo", 0); 132 prefs.edit().putLong("time", System.currentTimeMillis()).commit(); 133 } 134 }); 135 136 final Button writeLoopButton = (Button) findViewById(R.id.write_loop_button); 137 writeLoopButton.setOnClickListener(new View.OnClickListener() { 138 public void onClick(View v) { 139 long startTime = SystemClock.uptimeMillis(); 140 int iters = 1000; 141 BlockGuard.Policy policy = BlockGuard.getThreadPolicy(); 142 for (int i = 0; i < iters; ++i) { 143 policy.onWriteToDisk(); 144 } 145 long endTime = SystemClock.uptimeMillis(); 146 Log.d(TAG, "Time for " + iters + ": " + (endTime - startTime) + ", avg=" + 147 (endTime - startTime) / (double) iters); 148 } 149 }); 150 151 final Button dnsButton = (Button) findViewById(R.id.dns_button); 152 dnsButton.setOnClickListener(new View.OnClickListener() { 153 public void onClick(View v) { 154 Log.d(TAG, "Doing DNS lookup for www.l.google.com... " 155 + "(may be cached by InetAddress)"); 156 try { 157 InetAddress[] addrs = InetAddress.getAllByName("www.l.google.com"); 158 for (int i = 0; i < addrs.length; ++i) { 159 Log.d(TAG, "got: " + addrs[i]); 160 } 161 } catch (java.net.UnknownHostException e) { 162 Log.d(TAG, "DNS error: " + e); 163 } 164 165 // Now try a random hostname to evade libcore's 166 // DNS caching. 167 try { 168 String random = "" + Math.random(); 169 random = random.substring(random.indexOf(".") + 1); 170 String domain = random + ".livejournal.com"; 171 InetAddress addr = InetAddress.getByName(domain); 172 Log.d(TAG, "for random domain " + domain + ": " + addr); 173 } catch (java.net.UnknownHostException e) { 174 } 175 } 176 }); 177 178 final Button httpButton = (Button) findViewById(R.id.http_button); 179 httpButton.setOnClickListener(new View.OnClickListener() { 180 public void onClick(View v) { 181 try { 182 // Note: not using AndroidHttpClient, as that comes with its 183 // own pre-StrictMode network-on-Looper thread check. The 184 // intent of this test is that we test the network stack's 185 // instrumentation for StrictMode instead. 186 DefaultHttpClient httpClient = new DefaultHttpClient(); 187 HttpResponse res = httpClient.execute( 188 new HttpGet("http://www.android.com/favicon.ico")); 189 Log.d(TAG, "Fetched http response: " + res); 190 } catch (IOException e) { 191 Log.d(TAG, "HTTP fetch error: " + e); 192 } 193 } 194 }); 195 196 final Button http2Button = (Button) findViewById(R.id.http2_button); 197 http2Button.setOnClickListener(new View.OnClickListener() { 198 public void onClick(View v) { 199 try { 200 // Usually this ends up tripping in DNS resolution, 201 // so see http3Button below, which connects directly to an IP 202 InputStream is = new URL("http://www.android.com/") 203 .openConnection() 204 .getInputStream(); 205 Log.d(TAG, "Got input stream: " + is); 206 } catch (IOException e) { 207 Log.d(TAG, "HTTP fetch error: " + e); 208 } 209 } 210 }); 211 212 final Button http3Button = (Button) findViewById(R.id.http3_button); 213 http3Button.setOnClickListener(new View.OnClickListener() { 214 public void onClick(View v) { 215 try { 216 // One of Google's web IPs, as of 2010-06-16.... 217 InputStream is = new URL("http://74.125.19.14/") 218 .openConnection() 219 .getInputStream(); 220 Log.d(TAG, "Got input stream: " + is); 221 } catch (IOException e) { 222 Log.d(TAG, "HTTP fetch error: " + e); 223 } 224 } 225 }); 226 227 final Button binderLocalButton = (Button) findViewById(R.id.binder_local_button); 228 binderLocalButton.setOnClickListener(new View.OnClickListener() { 229 public void onClick(View v) { 230 try { 231 boolean value = mLocalServiceConn.stub.doDiskWrite(123 /* dummy */); 232 Log.d(TAG, "local writeToDisk returned: " + value); 233 } catch (RemoteException e) { 234 Log.d(TAG, "local binderButton error: " + e); 235 } 236 } 237 }); 238 239 final Button binderRemoteButton = (Button) findViewById(R.id.binder_remote_button); 240 binderRemoteButton.setOnClickListener(new View.OnClickListener() { 241 public void onClick(View v) { 242 try { 243 boolean value = mRemoteServiceConn.stub.doDiskWrite(1); 244 Log.d(TAG, "remote writeToDisk #1 returned: " + value); 245 value = mRemoteServiceConn.stub.doDiskWrite(2); 246 Log.d(TAG, "remote writeToDisk #2 returned: " + value); 247 } catch (RemoteException e) { 248 Log.d(TAG, "remote binderButton error: " + e); 249 } 250 } 251 }); 252 253 final Button binderOneWayButton = (Button) findViewById(R.id.binder_oneway_button); 254 binderOneWayButton.setOnClickListener(new View.OnClickListener() { 255 public void onClick(View v) { 256 try { 257 Log.d(TAG, "doing oneway disk write over Binder."); 258 mRemoteServiceConn.stub.doDiskOneWay(); 259 } catch (RemoteException e) { 260 Log.d(TAG, "remote binderButton error: " + e); 261 } 262 } 263 }); 264 265 final Button binderCheckButton = (Button) findViewById(R.id.binder_check_button); 266 binderCheckButton.setOnClickListener(new View.OnClickListener() { 267 public void onClick(View v) { 268 int policy; 269 try { 270 policy = mLocalServiceConn.stub.getThreadPolicy(); 271 Log.d(TAG, "local service policy: " + policy); 272 policy = mRemoteServiceConn.stub.getThreadPolicy(); 273 Log.d(TAG, "remote service policy: " + policy); 274 } catch (RemoteException e) { 275 Log.d(TAG, "binderCheckButton error: " + e); 276 } 277 } 278 }); 279 280 final Button serviceDumpButton = (Button) findViewById(R.id.service_dump); 281 serviceDumpButton.setOnClickListener(new View.OnClickListener() { 282 public void onClick(View v) { 283 Log.d(TAG, "About to do a service dump..."); 284 File file = new File("/sdcard/strictmode-service-dump.txt"); 285 FileOutputStream output = null; 286 final StrictMode.ThreadPolicy oldPolicy = StrictMode.getThreadPolicy(); 287 try { 288 StrictMode.setThreadPolicy(StrictMode.ThreadPolicy.LAX); 289 output = new FileOutputStream(file); 290 StrictMode.setThreadPolicy(oldPolicy); 291 boolean dumped = Debug.dumpService("cpuinfo", 292 output.getFD(), new String[0]); 293 Log.d(TAG, "Dumped = " + dumped); 294 } catch (IOException e) { 295 Log.e(TAG, "Can't dump service", e); 296 } finally { 297 StrictMode.setThreadPolicy(oldPolicy); 298 } 299 Log.d(TAG, "Did service dump."); 300 } 301 }); 302 303 final Button lingerCloseButton = (Button) findViewById(R.id.linger_close_button); 304 lingerCloseButton.setOnClickListener(new View.OnClickListener() { 305 public void onClick(View v) { 306 closeWithLinger(true); 307 } 308 }); 309 310 final Button nonlingerCloseButton = (Button) findViewById(R.id.nonlinger_close_button); 311 nonlingerCloseButton.setOnClickListener(new View.OnClickListener() { 312 public void onClick(View v) { 313 closeWithLinger(false); 314 } 315 }); 316 317 final Button leakCursorButton = (Button) findViewById(R.id.leak_cursor_button); 318 leakCursorButton.setOnClickListener(new View.OnClickListener() { 319 public void onClick(View v) { 320 final StrictMode.VmPolicy oldPolicy = StrictMode.getVmPolicy(); 321 try { 322 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() 323 .detectLeakedSqlLiteObjects() 324 .penaltyLog() 325 .penaltyDropBox() 326 .build()); 327 mDb.execSQL("CREATE TABLE IF NOT EXISTS FOO (a INT)"); 328 Cursor c = mDb.rawQuery("SELECT * FROM foo", null); 329 c = null; // never close it 330 Runtime.getRuntime().gc(); 331 } finally { 332 StrictMode.setVmPolicy(oldPolicy); 333 } 334 335 } 336 }); 337 338 final Button customButton = (Button) findViewById(R.id.custom_button); 339 customButton.setOnClickListener(new View.OnClickListener() { 340 public void onClick(View v) { 341 StrictMode.noteSlowCall("my example call"); 342 } 343 }); 344 345 final Button gcInstanceButton = (Button) findViewById(R.id.gc_instance_button); 346 gcInstanceButton.setOnClickListener(new View.OnClickListener() { 347 public void onClick(View v) { 348 ArrayList<DummyObject> list = new ArrayList<DummyObject>(); 349 list.add(new DummyObject()); 350 list.add(new DummyObject()); 351 352 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder(StrictMode.getVmPolicy()) 353 .setClassInstanceLimit(DummyObject.class, 1) 354 .penaltyLog() 355 .penaltyDropBox() 356 .build()); 357 StrictMode.conditionallyCheckInstanceCounts(); 358 list.clear(); 359 } 360 }); 361 362 final CheckBox checkNoWrite = (CheckBox) findViewById(R.id.policy_no_write); 363 final CheckBox checkNoRead = (CheckBox) findViewById(R.id.policy_no_reads); 364 final CheckBox checkNoNetwork = (CheckBox) findViewById(R.id.policy_no_network); 365 final CheckBox checkCustom = (CheckBox) findViewById(R.id.policy_custom); 366 final CheckBox checkPenaltyLog = (CheckBox) findViewById(R.id.policy_penalty_log); 367 final CheckBox checkPenaltyDialog = (CheckBox) findViewById(R.id.policy_penalty_dialog); 368 final CheckBox checkPenaltyDeath = (CheckBox) findViewById(R.id.policy_penalty_death); 369 final CheckBox checkPenaltyDropBox = (CheckBox) findViewById(R.id.policy_penalty_dropbox); 370 final CheckBox checkPenaltyFlash = (CheckBox) findViewById(R.id.policy_penalty_flash); 371 final CheckBox checkPenaltyNetworkDeath = (CheckBox) findViewById(R.id.policy_penalty_network_death); 372 373 View.OnClickListener changePolicy = new View.OnClickListener() { 374 public void onClick(View v) { 375 StrictMode.ThreadPolicy.Builder newPolicy = new StrictMode.ThreadPolicy.Builder(); 376 if (checkNoWrite.isChecked()) newPolicy.detectDiskWrites(); 377 if (checkNoRead.isChecked()) newPolicy.detectDiskReads(); 378 if (checkNoNetwork.isChecked()) newPolicy.detectNetwork(); 379 if (checkCustom.isChecked()) newPolicy.detectCustomSlowCalls(); 380 if (checkPenaltyLog.isChecked()) newPolicy.penaltyLog(); 381 if (checkPenaltyDialog.isChecked()) newPolicy.penaltyDialog(); 382 if (checkPenaltyDeath.isChecked()) newPolicy.penaltyDeath(); 383 if (checkPenaltyDropBox.isChecked()) newPolicy.penaltyDropBox(); 384 if (checkPenaltyFlash.isChecked()) newPolicy.penaltyFlashScreen(); 385 if (checkPenaltyNetworkDeath.isChecked()) newPolicy.penaltyDeathOnNetwork(); 386 StrictMode.ThreadPolicy policy = newPolicy.build(); 387 Log.v(TAG, "Changing policy to: " + policy); 388 StrictMode.setThreadPolicy(policy); 389 } 390 }; 391 checkNoWrite.setOnClickListener(changePolicy); 392 checkNoRead.setOnClickListener(changePolicy); 393 checkNoNetwork.setOnClickListener(changePolicy); 394 checkCustom.setOnClickListener(changePolicy); 395 checkPenaltyLog.setOnClickListener(changePolicy); 396 checkPenaltyDialog.setOnClickListener(changePolicy); 397 checkPenaltyDeath.setOnClickListener(changePolicy); 398 checkPenaltyDropBox.setOnClickListener(changePolicy); 399 checkPenaltyFlash.setOnClickListener(changePolicy); 400 checkPenaltyNetworkDeath.setOnClickListener(changePolicy); 401 } 402 403 @Override 404 public void onDestroy() { 405 super.onDestroy(); 406 mDb.close(); 407 mDb = null; 408 } 409 410 private void closeWithLinger(boolean linger) { 411 Log.d(TAG, "Socket linger test; linger=" + linger); 412 try { 413 Socket socket = new Socket(); 414 socket.setSoLinger(linger, 5); 415 socket.close(); 416 } catch (IOException e) { 417 Log.e(TAG, "Error with linger close", e); 418 } 419 } 420 421 private void fileReadLoop() { 422 RandomAccessFile raf = null; 423 File filename = getFileStreamPath("test.dat"); 424 try { 425 long sumNanos = 0; 426 byte[] buf = new byte[512]; 427 428 //raf = new RandomAccessFile(filename, "rw"); 429 //raf.write(buf); 430 //raf.close(); 431 //raf = null; 432 433 // The data's almost certainly cached -- it's not clear what we're testing here 434 raf = new RandomAccessFile(filename, "r"); 435 raf.seek(0); 436 raf.read(buf); 437 } catch (IOException e) { 438 Log.e(TAG, "File read failed", e); 439 } finally { 440 try { if (raf != null) raf.close(); } catch (IOException e) {} 441 } 442 } 443 444 // Returns milliseconds taken, or -1 on failure. 445 private long settingsWrite(int mode) { 446 Cursor c = null; 447 long startTime = SystemClock.uptimeMillis(); 448 // The database will take care of replacing duplicates. 449 try { 450 ContentValues values = new ContentValues(); 451 values.put("name", "dummy_for_testing"); 452 values.put("value", "" + startTime); 453 Uri uri = cr.insert(SYSTEM_SETTINGS_URI, values); 454 Log.v(TAG, "inserted uri: " + uri); 455 } catch (SQLException e) { 456 Log.w(TAG, "sqliteexception during write: " + e); 457 return -1; 458 } 459 long duration = SystemClock.uptimeMillis() - startTime; 460 return duration; 461 } 462 463 @Override public void onResume() { 464 super.onResume(); 465 bindService(new Intent(this, LocalService.class), 466 mLocalServiceConn, Context.BIND_AUTO_CREATE); 467 bindService(new Intent(this, RemoteService.class), 468 mRemoteServiceConn, Context.BIND_AUTO_CREATE); 469 } 470 471 @Override public void onPause() { 472 super.onPause(); 473 unbindService(mLocalServiceConn); 474 unbindService(mRemoteServiceConn); 475 } 476 477 private static class DummyObject { 478 int foo; 479 } 480} 481