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