1 /*
2  * Copyright (C) 2015 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 android.os.cts;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static com.google.common.truth.Truth.assertWithMessage;
21 
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.fail;
24 
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.ServiceConnection;
29 import android.content.pm.PackageManager;
30 import android.net.TrafficStats;
31 import android.net.Uri;
32 import android.os.IBinder;
33 import android.os.RemoteException;
34 import android.os.StrictMode;
35 import android.os.StrictMode.ThreadPolicy.Builder;
36 import android.os.StrictMode.ViolationInfo;
37 import android.os.strictmode.CustomViolation;
38 import android.os.strictmode.FileUriExposedViolation;
39 import android.os.strictmode.UntaggedSocketViolation;
40 import android.os.strictmode.Violation;
41 import android.support.test.InstrumentationRegistry;
42 import android.support.test.runner.AndroidJUnit4;
43 import android.system.Os;
44 import android.system.OsConstants;
45 import android.util.Log;
46 
47 import org.junit.After;
48 import org.junit.Before;
49 import org.junit.Test;
50 import org.junit.runner.RunWith;
51 
52 import java.io.File;
53 import java.io.FileDescriptor;
54 import java.io.FileInputStream;
55 import java.io.FileNotFoundException;
56 import java.io.FileOutputStream;
57 import java.io.IOException;
58 import java.net.HttpURLConnection;
59 import java.net.Socket;
60 import java.net.URL;
61 import java.util.ArrayList;
62 import java.util.List;
63 import java.util.concurrent.ArrayBlockingQueue;
64 import java.util.concurrent.BlockingQueue;
65 import java.util.concurrent.CountDownLatch;
66 import java.util.concurrent.ExecutionException;
67 import java.util.concurrent.LinkedBlockingQueue;
68 import java.util.concurrent.TimeUnit;
69 import java.util.function.Consumer;
70 
71 /** Tests for {@link StrictMode} */
72 @RunWith(AndroidJUnit4.class)
73 public class StrictModeTest {
74     private static final String TAG = "StrictModeTest";
75     private static final String REMOTE_SERVICE_ACTION = "android.app.REMOTESERVICE";
76 
77     private StrictMode.ThreadPolicy mThreadPolicy;
78     private StrictMode.VmPolicy mVmPolicy;
79 
getContext()80     private Context getContext() {
81         return InstrumentationRegistry.getContext();
82     }
83 
84     @Before
setUp()85     public void setUp() {
86         mThreadPolicy = StrictMode.getThreadPolicy();
87         mVmPolicy = StrictMode.getVmPolicy();
88     }
89 
90     @After
tearDown()91     public void tearDown() {
92         StrictMode.setThreadPolicy(mThreadPolicy);
93         StrictMode.setVmPolicy(mVmPolicy);
94     }
95 
96     public interface ThrowingRunnable {
run()97         void run() throws Exception;
98     }
99 
100     @Test
testThreadBuilder()101     public void testThreadBuilder() throws Exception {
102         StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().detectDiskReads().penaltyLog().build();
103         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder(policy).build());
104 
105         final File test = File.createTempFile("foo", "bar");
106         inspectViolation(
107                 test::exists,
108                 violation -> {
109                     assertThat(violation.getViolationDetails()).isNull();
110                     assertThat(violation.getStackTrace()).contains("DiskReadViolation");
111                 });
112     }
113 
114     @Test
testUnclosedCloseable()115     public void testUnclosedCloseable() throws Exception {
116         StrictMode.setVmPolicy(
117                 new StrictMode.VmPolicy.Builder().detectLeakedClosableObjects().build());
118 
119         inspectViolation(
120                 () -> leakCloseable("leaked.txt"),
121                 info -> {
122                     assertThat(info.getViolationDetails())
123                             .isEqualTo(
124                                     "A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.");
125                     assertThat(info.getStackTrace())
126                             .contains("Explicit termination method 'close' not called");
127                     assertThat(info.getStackTrace()).contains("leakCloseable");
128                     assertPolicy(info, StrictMode.DETECT_VM_CLOSABLE_LEAKS);
129                 });
130     }
131 
leakCloseable(String fileName)132     private void leakCloseable(String fileName) throws InterruptedException {
133         final CountDownLatch finalizedSignal = new CountDownLatch(1);
134         try {
135             new FileOutputStream(new File(getContext().getFilesDir(), fileName)) {
136                 @Override
137                 protected void finalize() throws IOException {
138                     super.finalize();
139                     finalizedSignal.countDown();
140                 }
141             };
142         } catch (FileNotFoundException e) {
143             throw new RuntimeException(e);
144         }
145         Runtime.getRuntime().gc();
146         Runtime.getRuntime().runFinalization();
147         // Sometimes it needs extra prodding.
148         if (!finalizedSignal.await(5, TimeUnit.SECONDS)) {
149             Runtime.getRuntime().gc();
150             Runtime.getRuntime().runFinalization();
151         }
152     }
153 
154     @Test
testClassInstanceLimit()155     public void testClassInstanceLimit() throws Exception {
156         StrictMode.setVmPolicy(
157                 new StrictMode.VmPolicy.Builder()
158                         .setClassInstanceLimit(LimitedClass.class, 1)
159                         .build());
160         List<LimitedClass> references = new ArrayList<>();
161         assertNoViolation(() -> references.add(new LimitedClass()));
162         references.add(new LimitedClass());
163         inspectViolation(
164                 StrictMode::conditionallyCheckInstanceCounts,
165                 info -> assertPolicy(info, StrictMode.DETECT_VM_INSTANCE_LEAKS));
166     }
167 
168     private static final class LimitedClass {}
169 
170     /** Insecure connection should be detected */
171     @Test
testCleartextNetwork()172     public void testCleartextNetwork() throws Exception {
173         if (!hasInternetConnection()) {
174             Log.i(TAG, "testCleartextNetwork() ignored on device without Internet");
175             return;
176         }
177 
178         StrictMode.setVmPolicy(
179                 new StrictMode.VmPolicy.Builder().detectCleartextNetwork().penaltyLog().build());
180 
181         inspectViolation(
182                 () ->
183                         ((HttpURLConnection) new URL("http://example.com/").openConnection())
184                                 .getResponseCode(),
185                 info -> {
186                     assertThat(info.getViolationDetails())
187                             .contains("Detected cleartext network traffic from UID");
188                     assertThat(info.getViolationDetails())
189                             .startsWith(StrictMode.CLEARTEXT_DETECTED_MSG);
190                     assertPolicy(info, StrictMode.DETECT_VM_CLEARTEXT_NETWORK);
191                 });
192     }
193 
194     /** Secure connection should be ignored */
195     @Test
testEncryptedNetwork()196     public void testEncryptedNetwork() throws Exception {
197         if (!hasInternetConnection()) {
198             Log.i(TAG, "testEncryptedNetwork() ignored on device without Internet");
199             return;
200         }
201 
202         StrictMode.setVmPolicy(
203                 new StrictMode.VmPolicy.Builder().detectCleartextNetwork().penaltyLog().build());
204 
205         assertNoViolation(
206                 () ->
207                         ((HttpURLConnection) new URL("https://example.com/").openConnection())
208                                 .getResponseCode());
209     }
210 
211     @Test
testFileUriExposure()212     public void testFileUriExposure() throws Exception {
213         StrictMode.setVmPolicy(
214                 new StrictMode.VmPolicy.Builder().detectFileUriExposure().penaltyLog().build());
215 
216         final Uri badUri = Uri.fromFile(new File("/sdcard/meow.jpg"));
217         inspectViolation(
218                 () -> {
219                     Intent intent = new Intent(Intent.ACTION_VIEW);
220                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
221                     intent.setDataAndType(badUri, "image/jpeg");
222                     getContext().startActivity(intent);
223                 },
224                 violation -> {
225                     assertThat(violation.getStackTrace()).contains(badUri + " exposed beyond app");
226                 });
227 
228         final Uri goodUri = Uri.parse("content://com.example/foobar");
229         assertNoViolation(
230                 () -> {
231                     Intent intent = new Intent(Intent.ACTION_VIEW);
232                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
233                     intent.setDataAndType(goodUri, "image/jpeg");
234                     getContext().startActivity(intent);
235                 });
236     }
237 
238     @Test
testContentUriWithoutPermission()239     public void testContentUriWithoutPermission() throws Exception {
240         StrictMode.setVmPolicy(
241                 new StrictMode.VmPolicy.Builder()
242                         .detectContentUriWithoutPermission()
243                         .penaltyLog()
244                         .build());
245 
246         final Uri uri = Uri.parse("content://com.example/foobar");
247         inspectViolation(
248                 () -> {
249                     Intent intent = new Intent(Intent.ACTION_VIEW);
250                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
251                     intent.setDataAndType(uri, "image/jpeg");
252                     getContext().startActivity(intent);
253                 },
254                 violation ->
255                         assertThat(violation.getStackTrace())
256                                 .contains(uri + " exposed beyond app"));
257 
258         assertNoViolation(
259                 () -> {
260                     Intent intent = new Intent(Intent.ACTION_VIEW);
261                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
262                     intent.setDataAndType(uri, "image/jpeg");
263                     intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
264                     getContext().startActivity(intent);
265                 });
266     }
267 
268     @Test
testUntaggedSocketsHttp()269     public void testUntaggedSocketsHttp() throws Exception {
270         if (!hasInternetConnection()) {
271             Log.i(TAG, "testUntaggedSockets() ignored on device without Internet");
272             return;
273         }
274 
275         StrictMode.setVmPolicy(
276                 new StrictMode.VmPolicy.Builder().detectUntaggedSockets().penaltyLog().build());
277 
278         inspectViolation(
279                 () ->
280                         ((HttpURLConnection) new URL("http://example.com/").openConnection())
281                                 .getResponseCode(),
282                 violation ->
283                         assertThat(violation.getStackTrace())
284                                 .contains(UntaggedSocketViolation.MESSAGE));
285 
286         assertNoViolation(
287                 () -> {
288                     TrafficStats.setThreadStatsTag(0xDECAFBAD);
289                     try {
290                         ((HttpURLConnection) new URL("http://example.com/").openConnection())
291                                 .getResponseCode();
292                     } finally {
293                         TrafficStats.clearThreadStatsTag();
294                     }
295                 });
296     }
297 
298     @Test
testUntaggedSocketsRaw()299     public void testUntaggedSocketsRaw() throws Exception {
300         if (!hasInternetConnection()) {
301             Log.i(TAG, "testUntaggedSockets() ignored on device without Internet");
302             return;
303         }
304 
305         StrictMode.setVmPolicy(
306                 new StrictMode.VmPolicy.Builder().detectUntaggedSockets().penaltyLog().build());
307 
308         assertNoViolation(
309                 () -> {
310                     TrafficStats.setThreadStatsTag(0xDECAFBAD);
311                     try (Socket socket = new Socket("example.com", 80)) {
312                         socket.getOutputStream().close();
313                     } finally {
314                         TrafficStats.clearThreadStatsTag();
315                     }
316                 });
317 
318         inspectViolation(
319                 () -> {
320                     try (Socket socket = new Socket("example.com", 80)) {
321                         socket.getOutputStream().close();
322                     }
323                 },
324                 violation ->
325                         assertThat(violation.getStackTrace())
326                                 .contains(UntaggedSocketViolation.MESSAGE));
327     }
328 
329     private static final int PERMISSION_USER_ONLY = 0600;
330 
331     @Test
testRead()332     public void testRead() throws Exception {
333         final File test = File.createTempFile("foo", "bar");
334         final File dir = test.getParentFile();
335 
336         final FileInputStream is = new FileInputStream(test);
337         final FileDescriptor fd =
338                 Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, PERMISSION_USER_ONLY);
339 
340         StrictMode.setThreadPolicy(
341                 new StrictMode.ThreadPolicy.Builder().detectDiskReads().penaltyLog().build());
342         inspectViolation(
343                 test::exists,
344                 violation -> {
345                     assertThat(violation.getViolationDetails()).isNull();
346                     assertThat(violation.getStackTrace()).contains("DiskReadViolation");
347                 });
348 
349         Consumer<ViolationInfo> assertDiskReadPolicy =
350                 violation -> assertPolicy(violation, StrictMode.DETECT_DISK_READ);
351         inspectViolation(test::exists, assertDiskReadPolicy);
352         inspectViolation(test::length, assertDiskReadPolicy);
353         inspectViolation(dir::list, assertDiskReadPolicy);
354         inspectViolation(is::read, assertDiskReadPolicy);
355 
356         inspectViolation(() -> new FileInputStream(test), assertDiskReadPolicy);
357         inspectViolation(
358                 () -> Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, PERMISSION_USER_ONLY),
359                 assertDiskReadPolicy);
360         inspectViolation(() -> Os.read(fd, new byte[10], 0, 1), assertDiskReadPolicy);
361     }
362 
363     @Test
testWrite()364     public void testWrite() throws Exception {
365         File file = File.createTempFile("foo", "bar");
366 
367         final FileOutputStream os = new FileOutputStream(file);
368         final FileDescriptor fd =
369                 Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, PERMISSION_USER_ONLY);
370 
371         StrictMode.setThreadPolicy(
372                 new StrictMode.ThreadPolicy.Builder().detectDiskWrites().penaltyLog().build());
373 
374         inspectViolation(
375                 file::createNewFile,
376                 violation -> {
377                     assertThat(violation.getViolationDetails()).isNull();
378                     assertThat(violation.getStackTrace()).contains("DiskWriteViolation");
379                 });
380 
381         Consumer<ViolationInfo> assertDiskWritePolicy =
382                 violation -> assertPolicy(violation, StrictMode.DETECT_DISK_WRITE);
383 
384         inspectViolation(() -> File.createTempFile("foo", "bar"), assertDiskWritePolicy);
385         inspectViolation(() -> new FileOutputStream(file), assertDiskWritePolicy);
386         inspectViolation(file::delete, assertDiskWritePolicy);
387         inspectViolation(file::createNewFile, assertDiskWritePolicy);
388         inspectViolation(() -> os.write(32), assertDiskWritePolicy);
389 
390         inspectViolation(
391                 () -> Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, PERMISSION_USER_ONLY),
392                 assertDiskWritePolicy);
393         inspectViolation(() -> Os.write(fd, new byte[10], 0, 1), assertDiskWritePolicy);
394         inspectViolation(() -> Os.fsync(fd), assertDiskWritePolicy);
395         inspectViolation(
396                 () -> file.renameTo(new File(file.getParent(), "foobar")), assertDiskWritePolicy);
397     }
398 
399     @Test
testNetwork()400     public void testNetwork() throws Exception {
401         if (!hasInternetConnection()) {
402             Log.i(TAG, "testUntaggedSockets() ignored on device without Internet");
403             return;
404         }
405 
406         StrictMode.setThreadPolicy(
407                 new StrictMode.ThreadPolicy.Builder().detectNetwork().penaltyLog().build());
408 
409         inspectViolation(
410                 () -> {
411                     try (Socket socket = new Socket("example.com", 80)) {
412                         socket.getOutputStream().close();
413                     }
414                 },
415                 violation -> assertPolicy(violation, StrictMode.DETECT_NETWORK));
416         inspectViolation(
417                 () ->
418                         ((HttpURLConnection) new URL("http://example.com/").openConnection())
419                                 .getResponseCode(),
420                 violation -> assertPolicy(violation, StrictMode.DETECT_NETWORK));
421     }
422 
423     @Test
testViolationAcrossBinder()424     public void testViolationAcrossBinder() throws Exception {
425         runWithRemoteServiceBound(
426                 getContext(),
427                 service -> {
428                     StrictMode.setThreadPolicy(
429                             new Builder().detectDiskWrites().penaltyLog().build());
430 
431                     try {
432                         inspectViolation(
433                                 () -> service.performDiskWrite(),
434                                 (violation) -> {
435                                     assertPolicy(violation, StrictMode.DETECT_DISK_WRITE);
436                                     assertThat(violation.getViolationDetails())
437                                             .isNull(); // Disk write has no message.
438                                     assertThat(violation.getStackTrace())
439                                             .contains("DiskWriteViolation");
440                                     assertThat(violation.getStackTrace())
441                                             .contains(
442                                                     "at android.os.StrictMode$AndroidBlockGuardPolicy.onWriteToDisk");
443                                     assertThat(violation.getStackTrace())
444                                             .contains("# via Binder call with stack:");
445                                     assertThat(violation.getStackTrace())
446                                             .contains(
447                                                     "at android.os.cts.ISecondary$Stub$Proxy.performDiskWrite");
448                                 });
449                         assertNoViolation(() -> service.getPid());
450                     } catch (Exception e) {
451                         throw new RuntimeException(e);
452                     }
453                 });
454     }
455 
checkNonSdkApiUsageViolation(boolean blacklist, String className, String methodName, Class<?>... paramTypes)456     private void checkNonSdkApiUsageViolation(boolean blacklist, String className,
457             String methodName, Class<?>... paramTypes) throws Exception {
458         Class<?> clazz = Class.forName(className);
459         inspectViolation(
460             () -> {
461                 try {
462                     java.lang.reflect.Method m = clazz.getDeclaredMethod(methodName, paramTypes);
463                     if (blacklist) {
464                         fail();
465                     }
466                 } catch (NoSuchMethodException expected) {
467                   if (!blacklist) {
468                     fail();
469                   }
470                 }
471             },
472             violation -> {
473                 assertThat(violation).isNotNull();
474                 assertPolicy(violation, StrictMode.DETECT_VM_NON_SDK_API_USAGE);
475                 assertThat(violation.getViolationDetails()).contains(methodName);
476                 assertThat(violation.getStackTrace()).contains("checkNonSdkApiUsageViolation");
477             }
478         );
479     }
480 
481     @Test
testNonSdkApiUsage()482     public void testNonSdkApiUsage() throws Exception {
483         StrictMode.VmPolicy oldVmPolicy = StrictMode.getVmPolicy();
484         StrictMode.ThreadPolicy oldThreadPolicy = StrictMode.getThreadPolicy();
485         try {
486             StrictMode.setVmPolicy(
487                     new StrictMode.VmPolicy.Builder().detectNonSdkApiUsage().build());
488             checkNonSdkApiUsageViolation(
489                 true, "dalvik.system.VMRuntime", "setHiddenApiExemptions", String[].class);
490             // verify that mutliple uses of a light greylist API are detected.
491             checkNonSdkApiUsageViolation(false, "dalvik.system.VMRuntime", "getRuntime");
492             checkNonSdkApiUsageViolation(false, "dalvik.system.VMRuntime", "getRuntime");
493 
494             // Verify that the VM policy is turned off after a call to permitNonSdkApiUsage.
495             StrictMode.setVmPolicy(
496                 new StrictMode.VmPolicy.Builder().permitNonSdkApiUsage().build());
497             assertNoViolation(() -> {
498                   Class<?> clazz = Class.forName("dalvik.system.VMRuntime");
499                   try {
500                       clazz.getDeclaredMethod("getRuntime");
501                   } catch (NoSuchMethodException maybe) {
502                   }
503             });
504         } finally {
505             StrictMode.setVmPolicy(oldVmPolicy);
506             StrictMode.setThreadPolicy(oldThreadPolicy);
507         }
508     }
509 
510     @Test
testThreadPenaltyListener()511     public void testThreadPenaltyListener() throws Exception {
512         final BlockingQueue<Violation> violations = new ArrayBlockingQueue<>(1);
513         StrictMode.setThreadPolicy(
514                 new StrictMode.ThreadPolicy.Builder().detectCustomSlowCalls()
515                         .penaltyListener(getContext().getMainExecutor(), (v) -> {
516                             violations.add(v);
517                         }).build());
518 
519         StrictMode.noteSlowCall("foo");
520 
521         final Violation v = violations.poll(5, TimeUnit.SECONDS);
522         assertTrue(v instanceof CustomViolation);
523     }
524 
525     @Test
testVmPenaltyListener()526     public void testVmPenaltyListener() throws Exception {
527         final BlockingQueue<Violation> violations = new ArrayBlockingQueue<>(1);
528         StrictMode.setVmPolicy(
529                 new StrictMode.VmPolicy.Builder().detectFileUriExposure()
530                         .penaltyListener(getContext().getMainExecutor(), (v) -> {
531                             violations.add(v);
532                         }).build());
533 
534         Intent intent = new Intent(Intent.ACTION_VIEW);
535         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
536         intent.setDataAndType(Uri.fromFile(new File("/sdcard/meow.jpg")), "image/jpeg");
537         getContext().startActivity(intent);
538 
539         final Violation v = violations.poll(5, TimeUnit.SECONDS);
540         assertTrue(v instanceof FileUriExposedViolation);
541     }
542 
runWithRemoteServiceBound(Context context, Consumer<ISecondary> consumer)543     private static void runWithRemoteServiceBound(Context context, Consumer<ISecondary> consumer)
544             throws ExecutionException, InterruptedException, RemoteException {
545         BlockingQueue<IBinder> binderHolder = new ArrayBlockingQueue<>(1);
546         ServiceConnection secondaryConnection =
547                 new ServiceConnection() {
548                     public void onServiceConnected(ComponentName className, IBinder service) {
549                         binderHolder.add(service);
550                     }
551 
552                     public void onServiceDisconnected(ComponentName className) {
553                         binderHolder.drainTo(new ArrayList<>());
554                     }
555                 };
556         Intent intent = new Intent(REMOTE_SERVICE_ACTION);
557         intent.setPackage(context.getPackageName());
558 
559         Intent secondaryIntent = new Intent(ISecondary.class.getName());
560         secondaryIntent.setPackage(context.getPackageName());
561         assertThat(
562                         context.bindService(
563                                 secondaryIntent, secondaryConnection, Context.BIND_AUTO_CREATE))
564                 .isTrue();
565         IBinder binder = binderHolder.take();
566         assertThat(binder.queryLocalInterface(binder.getInterfaceDescriptor())).isNull();
567         consumer.accept(ISecondary.Stub.asInterface(binder));
568         context.unbindService(secondaryConnection);
569         context.stopService(intent);
570     }
571 
assertViolation(String expected, ThrowingRunnable r)572     private static void assertViolation(String expected, ThrowingRunnable r) throws Exception {
573         inspectViolation(r, violation -> assertThat(violation.getStackTrace()).contains(expected));
574     }
575 
assertNoViolation(ThrowingRunnable r)576     private static void assertNoViolation(ThrowingRunnable r) throws Exception {
577         inspectViolation(
578                 r, violation -> assertWithMessage("Unexpected violation").that(violation).isNull());
579     }
580 
assertPolicy(ViolationInfo info, int policy)581     private void assertPolicy(ViolationInfo info, int policy) {
582         assertWithMessage("Policy bit incorrect").that(info.getViolationBit()).isEqualTo(policy);
583     }
584 
inspectViolation( ThrowingRunnable violating, Consumer<ViolationInfo> consume)585     private static void inspectViolation(
586             ThrowingRunnable violating, Consumer<ViolationInfo> consume) throws Exception {
587         final LinkedBlockingQueue<ViolationInfo> violations = new LinkedBlockingQueue<>();
588         StrictMode.setViolationLogger(violations::add);
589 
590         try {
591             violating.run();
592             consume.accept(violations.poll(5, TimeUnit.SECONDS));
593         } finally {
594             StrictMode.setViolationLogger(null);
595         }
596     }
597 
hasInternetConnection()598     private boolean hasInternetConnection() {
599         final PackageManager pm = getContext().getPackageManager();
600         return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
601                 || pm.hasSystemFeature(PackageManager.FEATURE_WIFI)
602                 || pm.hasSystemFeature(PackageManager.FEATURE_ETHERNET);
603     }
604 }
605