StrictModeActivity.java revision 330ecff66820f2f97f09699aadfbd1e0cd014fa1
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