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