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