• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.content.Context.WINDOW_SERVICE;
20 import static android.content.pm.PackageManager.FEATURE_INPUT_METHODS;
21 import static android.view.Display.DEFAULT_DISPLAY;
22 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
23 
24 import static com.android.cts.mockime.ImeEventStreamTestUtils.clearAllEvents;
25 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectCommand;
26 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
27 import static com.android.cts.mockime.ImeEventStreamTestUtils.notExpectEvent;
28 
29 import static com.google.common.truth.Truth.assertThat;
30 import static com.google.common.truth.Truth.assertWithMessage;
31 
32 import static org.junit.Assert.assertTrue;
33 import static org.junit.Assert.fail;
34 
35 import android.app.Activity;
36 import android.content.ComponentName;
37 import android.content.Context;
38 import android.content.Intent;
39 import android.content.ServiceConnection;
40 import android.content.pm.PackageManager;
41 import android.content.res.Configuration;
42 import android.hardware.display.DisplayManager;
43 import android.inputmethodservice.InputMethodService;
44 import android.net.TrafficStats;
45 import android.net.Uri;
46 import android.os.IBinder;
47 import android.os.RemoteException;
48 import android.os.StrictMode;
49 import android.os.StrictMode.ThreadPolicy.Builder;
50 import android.os.StrictMode.ViolationInfo;
51 import android.os.strictmode.CleartextNetworkViolation;
52 import android.os.strictmode.CustomViolation;
53 import android.os.strictmode.DiskReadViolation;
54 import android.os.strictmode.DiskWriteViolation;
55 import android.os.strictmode.ExplicitGcViolation;
56 import android.os.strictmode.FileUriExposedViolation;
57 import android.os.strictmode.InstanceCountViolation;
58 import android.os.strictmode.LeakedClosableViolation;
59 import android.os.strictmode.NetworkViolation;
60 import android.os.strictmode.NonSdkApiUsedViolation;
61 import android.os.strictmode.UntaggedSocketViolation;
62 import android.os.strictmode.Violation;
63 import android.platform.test.annotations.AppModeFull;
64 import android.platform.test.annotations.AppModeInstant;
65 import android.system.Os;
66 import android.system.OsConstants;
67 import android.util.Log;
68 import android.view.Display;
69 import android.view.ViewConfiguration;
70 import android.view.WindowManager;
71 
72 import androidx.annotation.IntDef;
73 import androidx.test.core.app.ApplicationProvider;
74 import androidx.test.platform.app.InstrumentationRegistry;
75 import androidx.test.runner.AndroidJUnit4;
76 
77 import com.android.cts.mockime.ImeEvent;
78 import com.android.cts.mockime.ImeEventStream;
79 import com.android.cts.mockime.ImeSettings;
80 import com.android.cts.mockime.MockImeSession;
81 
82 import org.junit.After;
83 import org.junit.Before;
84 import org.junit.Test;
85 import org.junit.runner.RunWith;
86 
87 import java.io.File;
88 import java.io.FileDescriptor;
89 import java.io.FileInputStream;
90 import java.io.FileNotFoundException;
91 import java.io.FileOutputStream;
92 import java.io.IOException;
93 import java.lang.annotation.Retention;
94 import java.lang.annotation.RetentionPolicy;
95 import java.net.HttpURLConnection;
96 import java.net.Socket;
97 import java.net.URL;
98 import java.util.ArrayList;
99 import java.util.List;
100 import java.util.concurrent.ArrayBlockingQueue;
101 import java.util.concurrent.BlockingQueue;
102 import java.util.concurrent.CountDownLatch;
103 import java.util.concurrent.ExecutionException;
104 import java.util.concurrent.LinkedBlockingQueue;
105 import java.util.concurrent.TimeUnit;
106 import java.util.function.Consumer;
107 
108 /** Tests for {@link StrictMode} */
109 @RunWith(AndroidJUnit4.class)
110 public class StrictModeTest {
111     private static final String TAG = "StrictModeTest";
112     private static final String REMOTE_SERVICE_ACTION = "android.app.REMOTESERVICE";
113     private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(10); // 10 seconds
114     private static final long NOT_EXPECT_TIMEOUT = TimeUnit.SECONDS.toMillis(2);
115 
116     private StrictMode.ThreadPolicy mThreadPolicy;
117     private StrictMode.VmPolicy mVmPolicy;
118 
119     // TODO(b/160143006): re-enable IMS part test.
120     private static final boolean DISABLE_VERIFY_IMS = false;
121 
122     /**
123      * Verify mode to verifying if APIs violates incorrect context violation.
124      *
125      * @see #VERIFY_MODE_GET_DISPLAY
126      * @see #VERIFY_MODE_GET_WINDOW_MANAGER
127      * @see #VERIFY_MODE_GET_VIEW_CONFIGURATION
128      */
129     @Retention(RetentionPolicy.SOURCE)
130     @IntDef(flag = true, value = {
131             VERIFY_MODE_GET_DISPLAY,
132             VERIFY_MODE_GET_WINDOW_MANAGER,
133             VERIFY_MODE_GET_VIEW_CONFIGURATION,
134     })
135     private @interface VerifyMode {}
136 
137     /**
138      * Verifies if {@link Context#getDisplay} from {@link InputMethodService} and context created
139      * from {@link InputMethodService#createConfigurationContext(Configuration)} violates
140      * incorrect context violation.
141      */
142     private static final int VERIFY_MODE_GET_DISPLAY = 1;
143     /**
144      * Verifies if get {@link android.view.WindowManager} from {@link InputMethodService} and
145      * context created from {@link InputMethodService#createConfigurationContext(Configuration)}
146      * violates incorrect context violation.
147      *
148      * @see Context#getSystemService(String)
149      * @see Context#getSystemService(Class)
150      */
151     private static final int VERIFY_MODE_GET_WINDOW_MANAGER = 2;
152     /**
153      * Verifies if passing {@link InputMethodService} and context created
154      * from {@link InputMethodService#createConfigurationContext(Configuration)} to
155      * {@link android.view.ViewConfiguration#get(Context)} violates incorrect context violation.
156      */
157     private static final int VERIFY_MODE_GET_VIEW_CONFIGURATION = 3;
158 
getContext()159     private Context getContext() {
160         return ApplicationProvider.getApplicationContext();
161     }
162 
163     @Before
setUp()164     public void setUp() {
165         mThreadPolicy = StrictMode.getThreadPolicy();
166         mVmPolicy = StrictMode.getVmPolicy();
167     }
168 
169     @After
tearDown()170     public void tearDown() {
171         StrictMode.setThreadPolicy(mThreadPolicy);
172         StrictMode.setVmPolicy(mVmPolicy);
173     }
174 
175     public interface ThrowingRunnable {
run()176         void run() throws Exception;
177     }
178 
179     @Test
testThreadBuilder()180     public void testThreadBuilder() throws Exception {
181         StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().detectDiskReads().penaltyLog().build();
182         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder(policy).build());
183 
184         final File test = File.createTempFile("foo", "bar");
185         inspectViolation(
186                 test::exists,
187                 info -> {
188                     assertThat(info.getViolationDetails()).isNull();
189                     assertThat(info.getStackTrace()).contains("DiskReadViolation");
190                 });
191     }
192 
193     @Test
testUnclosedCloseable()194     public void testUnclosedCloseable() throws Exception {
195         StrictMode.setVmPolicy(
196                 new StrictMode.VmPolicy.Builder().detectLeakedClosableObjects().build());
197 
198         inspectViolation(
199                 () -> leakCloseable("leaked.txt"),
200                 info -> {
201                     assertThat(info.getViolationDetails())
202                             .isEqualTo(
203                                     "A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.");
204                     assertThat(info.getStackTrace())
205                             .contains("Explicit termination method 'close' not called");
206                     assertThat(info.getStackTrace()).contains("leakCloseable");
207                     assertThat(info.getViolationClass())
208                             .isAssignableTo(LeakedClosableViolation.class);
209                 });
210     }
211 
leakCloseable(String fileName)212     private void leakCloseable(String fileName) throws InterruptedException {
213         final CountDownLatch finalizedSignal = new CountDownLatch(1);
214         try {
215             new FileOutputStream(new File(getContext().getFilesDir(), fileName)) {
216                 @Override
217                 protected void finalize() throws IOException {
218                     super.finalize();
219                     finalizedSignal.countDown();
220                 }
221             };
222         } catch (FileNotFoundException e) {
223             throw new RuntimeException(e);
224         }
225         Runtime.getRuntime().gc();
226         Runtime.getRuntime().runFinalization();
227         // Sometimes it needs extra prodding.
228         if (!finalizedSignal.await(5, TimeUnit.SECONDS)) {
229             Runtime.getRuntime().gc();
230             Runtime.getRuntime().runFinalization();
231         }
232     }
233 
234     @Test
testClassInstanceLimit()235     public void testClassInstanceLimit() throws Exception {
236         StrictMode.setVmPolicy(
237                 new StrictMode.VmPolicy.Builder()
238                         .setClassInstanceLimit(LimitedClass.class, 1)
239                         .build());
240         List<LimitedClass> references = new ArrayList<>();
241         assertNoViolation(() -> references.add(new LimitedClass()));
242         references.add(new LimitedClass());
243         inspectViolation(
244                 StrictMode::conditionallyCheckInstanceCounts,
245                 info -> assertThat(info.getViolationClass())
246                         .isAssignableTo(InstanceCountViolation.class));
247     }
248 
249     private static final class LimitedClass {}
250 
251     /** Insecure connection should be detected */
252     @AppModeFull
253     @Test
testCleartextNetwork()254     public void testCleartextNetwork() throws Exception {
255         if (!hasInternetConnection()) {
256             Log.i(TAG, "testCleartextNetwork() ignored on device without Internet");
257             return;
258         }
259 
260         StrictMode.setVmPolicy(
261                 new StrictMode.VmPolicy.Builder().detectCleartextNetwork().penaltyLog().build());
262 
263         inspectViolation(
264                 () ->
265                         ((HttpURLConnection) new URL("http://example.com/").openConnection())
266                                 .getResponseCode(),
267                 info -> assertThat(info.getViolationClass())
268                         .isAssignableTo(CleartextNetworkViolation.class));
269     }
270 
271     /** Secure connection should be ignored */
272     @Test
testEncryptedNetwork()273     public void testEncryptedNetwork() throws Exception {
274         if (!hasInternetConnection()) {
275             Log.i(TAG, "testEncryptedNetwork() ignored on device without Internet");
276             return;
277         }
278 
279         StrictMode.setVmPolicy(
280                 new StrictMode.VmPolicy.Builder().detectCleartextNetwork().penaltyLog().build());
281 
282         assertNoViolation(
283                 () ->
284                         ((HttpURLConnection) new URL("https://example.com/").openConnection())
285                                 .getResponseCode());
286     }
287 
288     @Test
testFileUriExposure()289     public void testFileUriExposure() throws Exception {
290         StrictMode.setVmPolicy(
291                 new StrictMode.VmPolicy.Builder().detectFileUriExposure().penaltyLog().build());
292 
293         final Uri badUri = Uri.fromFile(new File("/sdcard/meow.jpg"));
294         inspectViolation(
295                 () -> {
296                     Intent intent = new Intent(Intent.ACTION_VIEW);
297                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
298                     intent.setDataAndType(badUri, "image/jpeg");
299                     getContext().startActivity(intent);
300                 },
301                 info -> {
302                     assertThat(info.getStackTrace()).contains(badUri + " exposed beyond app");
303                 });
304 
305         final Uri goodUri = Uri.parse("content://com.example/foobar");
306         assertNoViolation(
307                 () -> {
308                     Intent intent = new Intent(Intent.ACTION_VIEW);
309                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
310                     intent.setDataAndType(goodUri, "image/jpeg");
311                     getContext().startActivity(intent);
312                 });
313     }
314 
315     @Test
testFileUriExposure_Chooser()316     public void testFileUriExposure_Chooser() throws Exception {
317         StrictMode.setVmPolicy(
318                 new StrictMode.VmPolicy.Builder().detectFileUriExposure().penaltyLog().build());
319 
320         final Uri badUri = Uri.fromFile(new File("/sdcard/meow.jpg"));
321         inspectViolation(
322                 () -> {
323                     Intent intent = new Intent(Intent.ACTION_SEND);
324                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
325                     intent.setType("image/jpeg");
326                     intent.putExtra(Intent.EXTRA_STREAM, badUri);
327 
328                     Intent chooser = Intent.createChooser(intent, "CTS");
329                     chooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
330                     getContext().startActivity(chooser);
331                 },
332                 info -> {
333                     assertThat(info.getStackTrace()).contains(badUri + " exposed beyond app");
334                 });
335     }
336 
337     @Test
testContentUriWithoutPermission()338     public void testContentUriWithoutPermission() throws Exception {
339         StrictMode.setVmPolicy(
340                 new StrictMode.VmPolicy.Builder()
341                         .detectContentUriWithoutPermission()
342                         .penaltyLog()
343                         .build());
344 
345         final Uri uri = Uri.parse("content://com.example/foobar");
346         inspectViolation(
347                 () -> {
348                     Intent intent = new Intent(Intent.ACTION_VIEW);
349                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
350                     intent.setDataAndType(uri, "image/jpeg");
351                     getContext().startActivity(intent);
352                 },
353                 info ->
354                         assertThat(info.getStackTrace())
355                                 .contains(uri + " exposed beyond app"));
356 
357         assertNoViolation(
358                 () -> {
359                     Intent intent = new Intent(Intent.ACTION_VIEW);
360                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
361                     intent.setDataAndType(uri, "image/jpeg");
362                     intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
363                     getContext().startActivity(intent);
364                 });
365     }
366 
367     @AppModeFull
368     @Test
testUntaggedSocketsHttp()369     public void testUntaggedSocketsHttp() throws Exception {
370         if (!hasInternetConnection()) {
371             Log.i(TAG, "testUntaggedSockets() ignored on device without Internet");
372             return;
373         }
374 
375         StrictMode.setVmPolicy(
376                 new StrictMode.VmPolicy.Builder().detectUntaggedSockets().penaltyLog().build());
377 
378         inspectViolation(
379                 () ->
380                         ((HttpURLConnection) new URL("http://example.com/").openConnection())
381                                 .getResponseCode(),
382                 info -> assertThat(info.getViolationClass())
383                         .isAssignableTo(UntaggedSocketViolation.class));
384 
385         assertNoViolation(
386                 () -> {
387                     TrafficStats.setThreadStatsTag(0xDECAFBAD);
388                     try {
389                         ((HttpURLConnection) new URL("http://example.com/").openConnection())
390                                 .getResponseCode();
391                     } finally {
392                         TrafficStats.clearThreadStatsTag();
393                     }
394                 });
395     }
396 
397     @Test
testUntaggedSocketsRaw()398     public void testUntaggedSocketsRaw() throws Exception {
399         if (!hasInternetConnection()) {
400             Log.i(TAG, "testUntaggedSockets() ignored on device without Internet");
401             return;
402         }
403 
404         StrictMode.setVmPolicy(
405                 new StrictMode.VmPolicy.Builder().detectUntaggedSockets().penaltyLog().build());
406 
407         assertNoViolation(
408                 () -> {
409                     TrafficStats.setThreadStatsTag(0xDECAFBAD);
410                     try (Socket socket = new Socket("example.com", 80)) {
411                         socket.getOutputStream().close();
412                     } finally {
413                         TrafficStats.clearThreadStatsTag();
414                     }
415                 });
416 
417         inspectViolation(
418                 () -> {
419                     try (Socket socket = new Socket("example.com", 80)) {
420                         socket.getOutputStream().close();
421                     }
422                 },
423                 info -> assertThat(info.getViolationClass())
424                         .isAssignableTo(UntaggedSocketViolation.class));
425     }
426 
427     private static final int PERMISSION_USER_ONLY = 0600;
428 
429     @Test
testRead()430     public void testRead() throws Exception {
431         final File test = File.createTempFile("foo", "bar");
432         final File dir = test.getParentFile();
433 
434         final FileInputStream is = new FileInputStream(test);
435         final FileDescriptor fd =
436                 Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, PERMISSION_USER_ONLY);
437 
438         StrictMode.setThreadPolicy(
439                 new StrictMode.ThreadPolicy.Builder().detectDiskReads().penaltyLog().build());
440         inspectViolation(
441                 test::exists,
442                 info -> {
443                     assertThat(info.getViolationDetails()).isNull();
444                     assertThat(info.getStackTrace()).contains("DiskReadViolation");
445                 });
446 
447         Consumer<ViolationInfo> assertDiskReadPolicy = info -> assertThat(
448                 info.getViolationClass()).isAssignableTo(DiskReadViolation.class);
449         inspectViolation(test::exists, assertDiskReadPolicy);
450         inspectViolation(test::length, assertDiskReadPolicy);
451         inspectViolation(dir::list, assertDiskReadPolicy);
452         inspectViolation(is::read, assertDiskReadPolicy);
453 
454         inspectViolation(() -> new FileInputStream(test), assertDiskReadPolicy);
455         inspectViolation(
456                 () -> Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, PERMISSION_USER_ONLY),
457                 assertDiskReadPolicy);
458         inspectViolation(() -> Os.read(fd, new byte[10], 0, 1), assertDiskReadPolicy);
459     }
460 
461     @Test
testWrite()462     public void testWrite() throws Exception {
463         File file = File.createTempFile("foo", "bar");
464 
465         final FileOutputStream os = new FileOutputStream(file);
466         final FileDescriptor fd =
467                 Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, PERMISSION_USER_ONLY);
468 
469         StrictMode.setThreadPolicy(
470                 new StrictMode.ThreadPolicy.Builder().detectDiskWrites().penaltyLog().build());
471 
472         inspectViolation(
473                 file::createNewFile,
474                 info -> {
475                     assertThat(info.getViolationDetails()).isNull();
476                     assertThat(info.getStackTrace()).contains("DiskWriteViolation");
477                 });
478 
479         Consumer<ViolationInfo> assertDiskWritePolicy = info -> assertThat(
480                 info.getViolationClass()).isAssignableTo(DiskWriteViolation.class);
481 
482         inspectViolation(() -> File.createTempFile("foo", "bar"), assertDiskWritePolicy);
483         inspectViolation(() -> new FileOutputStream(file), assertDiskWritePolicy);
484         inspectViolation(file::delete, assertDiskWritePolicy);
485         inspectViolation(file::createNewFile, assertDiskWritePolicy);
486         inspectViolation(() -> os.write(32), assertDiskWritePolicy);
487 
488         inspectViolation(
489                 () -> Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, PERMISSION_USER_ONLY),
490                 assertDiskWritePolicy);
491         inspectViolation(() -> Os.write(fd, new byte[10], 0, 1), assertDiskWritePolicy);
492         inspectViolation(() -> Os.fsync(fd), assertDiskWritePolicy);
493         inspectViolation(
494                 () -> file.renameTo(new File(file.getParent(), "foobar")), assertDiskWritePolicy);
495     }
496 
497     @AppModeFull
498     @Test
testNetwork()499     public void testNetwork() throws Exception {
500         if (!hasInternetConnection()) {
501             Log.i(TAG, "testUntaggedSockets() ignored on device without Internet");
502             return;
503         }
504 
505         StrictMode.setThreadPolicy(
506                 new StrictMode.ThreadPolicy.Builder().detectNetwork().penaltyLog().build());
507 
508         inspectViolation(
509                 () -> {
510                     try (Socket socket = new Socket("example.com", 80)) {
511                         socket.getOutputStream().close();
512                     }
513                 },
514                 info -> assertThat(info.getViolationClass())
515                         .isAssignableTo(NetworkViolation.class));
516         inspectViolation(
517                 () ->
518                         ((HttpURLConnection) new URL("http://example.com/").openConnection())
519                                 .getResponseCode(),
520                 info -> assertThat(info.getViolationClass())
521                         .isAssignableTo(NetworkViolation.class));
522     }
523 
524     @Test
testExplicitGc()525     public void testExplicitGc() throws Exception {
526         StrictMode.setThreadPolicy(
527                 new StrictMode.ThreadPolicy.Builder().detectExplicitGc().penaltyLog().build());
528 
529         inspectViolation(
530                 () -> { Runtime.getRuntime().gc(); },
531                 info -> assertThat(info.getViolationClass())
532                         .isAssignableTo(ExplicitGcViolation.class));
533     }
534 
535     @Test
testViolationAcrossBinder()536     public void testViolationAcrossBinder() throws Exception {
537         runWithRemoteServiceBound(
538                 getContext(),
539                 service -> {
540                     StrictMode.setThreadPolicy(
541                             new Builder().detectDiskWrites().penaltyLog().build());
542 
543                     try {
544                         inspectViolation(
545                                 () -> service.performDiskWrite(),
546                                 (info) -> {
547                                     assertThat(info.getViolationClass())
548                                             .isAssignableTo(DiskWriteViolation.class);
549                                     assertThat(info.getViolationDetails())
550                                             .isNull(); // Disk write has no message.
551                                     assertThat(info.getStackTrace())
552                                             .contains("DiskWriteViolation");
553                                     assertThat(info.getStackTrace())
554                                             .contains(
555                                                     "at android.os.StrictMode$AndroidBlockGuardPolicy.onWriteToDisk");
556                                     assertThat(info.getStackTrace())
557                                             .contains("# via Binder call with stack:");
558                                     assertThat(info.getStackTrace())
559                                             .contains(
560                                                     "at android.os.cts.ISecondary$Stub$Proxy.performDiskWrite");
561                                 });
562                         assertNoViolation(() -> service.getPid());
563                     } catch (Exception e) {
564                         throw new RuntimeException(e);
565                     }
566                 });
567     }
568 
checkNonSdkApiUsageViolation(boolean blacklist, String className, String methodName, Class<?>... paramTypes)569     private void checkNonSdkApiUsageViolation(boolean blacklist, String className,
570             String methodName, Class<?>... paramTypes) throws Exception {
571         Class<?> clazz = Class.forName(className);
572         inspectViolation(
573             () -> {
574                 try {
575                     java.lang.reflect.Method m = clazz.getDeclaredMethod(methodName, paramTypes);
576                     if (blacklist) {
577                         fail();
578                     }
579                 } catch (NoSuchMethodException expected) {
580                   if (!blacklist) {
581                     fail();
582                   }
583                 }
584             },
585             info -> {
586                 assertThat(info).isNotNull();
587                 assertThat(info.getViolationClass())
588                         .isAssignableTo(NonSdkApiUsedViolation.class);
589                 assertThat(info.getViolationDetails()).contains(methodName);
590                 assertThat(info.getStackTrace()).contains("checkNonSdkApiUsageViolation");
591             }
592         );
593     }
594 
595     @Test
testNonSdkApiUsage()596     public void testNonSdkApiUsage() throws Exception {
597         StrictMode.VmPolicy oldVmPolicy = StrictMode.getVmPolicy();
598         StrictMode.ThreadPolicy oldThreadPolicy = StrictMode.getThreadPolicy();
599         try {
600             StrictMode.setVmPolicy(
601                     new StrictMode.VmPolicy.Builder().detectNonSdkApiUsage().build());
602             checkNonSdkApiUsageViolation(
603                 true, "dalvik.system.VMRuntime", "setHiddenApiExemptions", String[].class);
604             // verify that mutliple uses of a light greylist API are detected.
605             checkNonSdkApiUsageViolation(false, "dalvik.system.VMRuntime", "getRuntime");
606             checkNonSdkApiUsageViolation(false, "dalvik.system.VMRuntime", "getRuntime");
607 
608             // Verify that the VM policy is turned off after a call to permitNonSdkApiUsage.
609             StrictMode.setVmPolicy(
610                 new StrictMode.VmPolicy.Builder().permitNonSdkApiUsage().build());
611             assertNoViolation(() -> {
612                   Class<?> clazz = Class.forName("dalvik.system.VMRuntime");
613                   try {
614                       clazz.getDeclaredMethod("getRuntime");
615                   } catch (NoSuchMethodException maybe) {
616                   }
617             });
618         } finally {
619             StrictMode.setVmPolicy(oldVmPolicy);
620             StrictMode.setThreadPolicy(oldThreadPolicy);
621         }
622     }
623 
624     @Test
testThreadPenaltyListener()625     public void testThreadPenaltyListener() throws Exception {
626         final BlockingQueue<Violation> violations = new ArrayBlockingQueue<>(1);
627         StrictMode.setThreadPolicy(
628                 new StrictMode.ThreadPolicy.Builder().detectCustomSlowCalls()
629                         .penaltyListener(getContext().getMainExecutor(), (v) -> {
630                             violations.add(v);
631                         }).build());
632 
633         StrictMode.noteSlowCall("foo");
634 
635         final Violation v = violations.poll(5, TimeUnit.SECONDS);
636         assertTrue(v instanceof CustomViolation);
637     }
638 
639     @Test
testVmPenaltyListener()640     public void testVmPenaltyListener() throws Exception {
641         final BlockingQueue<Violation> violations = new ArrayBlockingQueue<>(1);
642         StrictMode.setVmPolicy(
643                 new StrictMode.VmPolicy.Builder().detectFileUriExposure()
644                         .penaltyListener(getContext().getMainExecutor(), (v) -> {
645                             violations.add(v);
646                         }).build());
647 
648         Intent intent = new Intent(Intent.ACTION_VIEW);
649         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
650         intent.setDataAndType(Uri.fromFile(new File("/sdcard/meow.jpg")), "image/jpeg");
651         getContext().startActivity(intent);
652 
653         final Violation v = violations.poll(5, TimeUnit.SECONDS);
654         assertTrue(v instanceof FileUriExposedViolation);
655     }
656 
657     @AppModeInstant
658     @Test
testNoCleartextHttpTrafficAllowed()659     public void testNoCleartextHttpTrafficAllowed() throws Exception {
660         if (!hasInternetConnection()) {
661             Log.i(TAG, "testNoCleartextHttpTrafficAllowed() ignored on device without Internet");
662             return;
663         }
664 
665         StrictMode.setVmPolicy(
666                 new StrictMode.VmPolicy.Builder().detectCleartextNetwork().penaltyLog().build());
667 
668         try {
669             inspectViolation(
670                     () ->
671                             ((HttpURLConnection) new URL("http://example.com/").openConnection())
672                                     .getResponseCode(),
673                     info -> assertThat(info.getViolationClass())
674                             .isAssignableTo(CleartextNetworkViolation.class));
675             fail("Instant app was able to send cleartext http traffic.");
676         } catch (IOException ex) {
677             // Expected
678         }
679     }
680 
681     @Test
testIncorrectContextUse_GetSystemService()682     public void testIncorrectContextUse_GetSystemService() throws Exception {
683         StrictMode.setVmPolicy(
684                 new StrictMode.VmPolicy.Builder()
685                         .detectIncorrectContextUse()
686                         .penaltyLog()
687                         .build());
688 
689         final String wmClassName = WindowManager.class.getSimpleName();
690         inspectViolation(
691                 () -> getContext().getApplicationContext().getSystemService(WindowManager.class),
692                 info -> assertThat(info.getStackTrace()).contains(
693                         "Tried to access visual service " + wmClassName));
694 
695         final Display display = getContext().getSystemService(DisplayManager.class)
696                 .getDisplay(DEFAULT_DISPLAY);
697         final Context visualContext = getContext().createDisplayContext(display)
698                 .createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */);
699         assertNoViolation(() -> visualContext.getSystemService(WINDOW_SERVICE));
700 
701         Intent intent = new Intent(getContext(), SimpleTestActivity.class);
702         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
703         final Activity activity = InstrumentationRegistry.getInstrumentation()
704                 .startActivitySync(intent);
705         assertNoViolation(() -> activity.getSystemService(WINDOW_SERVICE));
706 
707         // TODO(b/159593676): move the logic to CtsInputMethodTestCases
708         verifyIms(VERIFY_MODE_GET_WINDOW_MANAGER);
709     }
710 
711     @Test
testIncorrectContextUse_GetDisplay()712     public void testIncorrectContextUse_GetDisplay() throws Exception {
713         StrictMode.setVmPolicy(
714                 new StrictMode.VmPolicy.Builder()
715                         .detectIncorrectContextUse()
716                         .penaltyLog()
717                         .build());
718 
719         final Display display = getContext().getSystemService(DisplayManager.class)
720                 .getDisplay(DEFAULT_DISPLAY);
721 
722         final Context displayContext = getContext().createDisplayContext(display);
723         assertNoViolation(displayContext::getDisplay);
724 
725         final Context windowContext =
726                 displayContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */);
727         assertNoViolation(windowContext::getDisplay);
728 
729         Intent intent = new Intent(getContext(), SimpleTestActivity.class);
730         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
731 
732         final Activity activity = InstrumentationRegistry.getInstrumentation()
733                 .startActivitySync(intent);
734         assertNoViolation(() -> activity.getDisplay());
735 
736         // TODO(b/159593676): move the logic to CtsInputMethodTestCases
737         verifyIms(VERIFY_MODE_GET_DISPLAY);
738         try {
739             getContext().getApplicationContext().getDisplay();
740         } catch (UnsupportedOperationException e) {
741             return;
742         }
743         fail("Expected to get incorrect use exception from calling getDisplay() on Application");
744     }
745 
746     @Test
testIncorrectContextUse_GetViewConfiguration()747     public void testIncorrectContextUse_GetViewConfiguration() throws Exception {
748         StrictMode.setVmPolicy(
749                 new StrictMode.VmPolicy.Builder()
750                         .detectIncorrectContextUse()
751                         .penaltyLog()
752                         .build());
753 
754         final Context baseContext = getContext();
755         assertViolation(
756                 "Tried to access UI constants from a non-visual Context:",
757                 () -> ViewConfiguration.get(baseContext));
758 
759         final Display display = baseContext.getSystemService(DisplayManager.class)
760                 .getDisplay(DEFAULT_DISPLAY);
761         final Context displayContext = baseContext.createDisplayContext(display);
762         assertViolation(
763                 "Tried to access UI constants from a non-visual Context:",
764                 () -> ViewConfiguration.get(displayContext));
765 
766         final Context windowContext =
767                 displayContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */);
768         assertNoViolation(() -> ViewConfiguration.get(windowContext));
769 
770         Intent intent = new Intent(baseContext, SimpleTestActivity.class);
771         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
772         final Activity activity = InstrumentationRegistry.getInstrumentation()
773                 .startActivitySync(intent);
774         assertNoViolation(() -> ViewConfiguration.get(activity));
775 
776         // TODO(b/159593676): move the logic to CtsInputMethodTestCases
777         verifyIms(VERIFY_MODE_GET_VIEW_CONFIGURATION);
778     }
779 
780     // TODO(b/159593676): move the logic to CtsInputMethodTestCases
781     /**
782      * Verify if APIs violates incorrect context violations by {@code mode}.
783      *
784      * @see VerifyMode
785      */
verifyIms(@erifyMode int mode)786     private void verifyIms(@VerifyMode int mode) throws Exception {
787         // If devices do not support installable IMEs, finish the test gracefully. We don't use
788         // assumeTrue here because we do pass some cases, so showing "pass" instead of "skip" makes
789         // sense here.
790         // TODO(b/160143006): re-enable IMS part test.
791         if (!supportsInstallableIme() || DISABLE_VERIFY_IMS) {
792             return;
793         }
794 
795         try (final MockImeSession imeSession = MockImeSession.create(getContext(),
796                 InstrumentationRegistry.getInstrumentation().getUiAutomation(),
797                 new ImeSettings.Builder().setStrictModeEnabled(true))) {
798             final ImeEventStream stream = imeSession.openEventStream();
799             expectEvent(stream, event -> "onStartInput".equals(event.getEventName()), TIMEOUT);
800             final ImeEventStream forkedStream = clearAllEvents(stream, "onStrictModeViolated");
801             final ImeEvent imeEvent;
802             switch (mode) {
803                 case VERIFY_MODE_GET_DISPLAY:
804                     imeEvent = expectCommand(forkedStream, imeSession.callVerifyGetDisplay(),
805                             TIMEOUT);
806                     break;
807                 case VERIFY_MODE_GET_WINDOW_MANAGER:
808                     imeEvent = expectCommand(forkedStream, imeSession.callVerifyGetWindowManager(),
809                             TIMEOUT);
810                     break;
811                 case VERIFY_MODE_GET_VIEW_CONFIGURATION:
812                     imeEvent = expectCommand(forkedStream,
813                             imeSession.callVerifyGetViewConfiguration(), TIMEOUT);
814                     break;
815                 default:
816                     imeEvent = null;
817             }
818             assertTrue(imeEvent.getReturnBooleanValue());
819             notExpectEvent(stream, event -> "onStrictModeViolated".equals(event.getEventName()),
820                     NOT_EXPECT_TIMEOUT);
821         }
822     }
823 
supportsInstallableIme()824     private boolean supportsInstallableIme() {
825         return getContext().getPackageManager().hasSystemFeature(FEATURE_INPUT_METHODS);
826     }
827 
runWithRemoteServiceBound(Context context, Consumer<ISecondary> consumer)828     private static void runWithRemoteServiceBound(Context context, Consumer<ISecondary> consumer)
829             throws ExecutionException, InterruptedException, RemoteException {
830         BlockingQueue<IBinder> binderHolder = new ArrayBlockingQueue<>(1);
831         ServiceConnection secondaryConnection =
832                 new ServiceConnection() {
833                     public void onServiceConnected(ComponentName className, IBinder service) {
834                         binderHolder.add(service);
835                     }
836 
837                     public void onServiceDisconnected(ComponentName className) {
838                         binderHolder.drainTo(new ArrayList<>());
839                     }
840                 };
841         Intent intent = new Intent(REMOTE_SERVICE_ACTION);
842         intent.setPackage(context.getPackageName());
843 
844         Intent secondaryIntent = new Intent(ISecondary.class.getName());
845         secondaryIntent.setPackage(context.getPackageName());
846         assertThat(
847                         context.bindService(
848                                 secondaryIntent, secondaryConnection, Context.BIND_AUTO_CREATE))
849                 .isTrue();
850         IBinder binder = binderHolder.take();
851         assertThat(binder.queryLocalInterface(binder.getInterfaceDescriptor())).isNull();
852         consumer.accept(ISecondary.Stub.asInterface(binder));
853         context.unbindService(secondaryConnection);
854         context.stopService(intent);
855     }
856 
assertViolation(String expected, ThrowingRunnable r)857     private static void assertViolation(String expected, ThrowingRunnable r) throws Exception {
858         inspectViolation(r, info -> assertThat(info.getStackTrace()).contains(expected));
859     }
860 
assertNoViolation(ThrowingRunnable r)861     private static void assertNoViolation(ThrowingRunnable r) throws Exception {
862         inspectViolation(
863                 r, info -> assertWithMessage("Unexpected violation").that(info).isNull());
864     }
865 
inspectViolation( ThrowingRunnable violating, Consumer<ViolationInfo> consume)866     private static void inspectViolation(
867             ThrowingRunnable violating, Consumer<ViolationInfo> consume) throws Exception {
868         final LinkedBlockingQueue<ViolationInfo> violations = new LinkedBlockingQueue<>();
869         StrictMode.setViolationLogger(violations::add);
870 
871         try {
872             violating.run();
873             consume.accept(violations.poll(5, TimeUnit.SECONDS));
874         } finally {
875             StrictMode.setViolationLogger(null);
876         }
877     }
878 
hasInternetConnection()879     private boolean hasInternetConnection() {
880         final PackageManager pm = getContext().getPackageManager();
881         return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
882                 || pm.hasSystemFeature(PackageManager.FEATURE_WIFI)
883                 || pm.hasSystemFeature(PackageManager.FEATURE_ETHERNET);
884     }
885 }
886