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