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.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
20 import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
21 import static android.content.Context.WINDOW_SERVICE;
22 import static android.view.Display.DEFAULT_DISPLAY;
23 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
24 import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
25 
26 import static com.google.common.truth.Truth.assertThat;
27 import static com.google.common.truth.Truth.assertWithMessage;
28 
29 import static org.junit.Assert.assertTrue;
30 import static org.junit.Assert.fail;
31 
32 import android.app.Activity;
33 import android.app.Instrumentation;
34 import android.app.Service;
35 import android.app.WallpaperManager;
36 import android.content.ComponentName;
37 import android.content.Context;
38 import android.content.Intent;
39 import android.content.IntentFilter;
40 import android.content.ServiceConnection;
41 import android.content.pm.PackageManager;
42 import android.content.res.Configuration;
43 import android.hardware.display.DisplayManager;
44 import android.net.TrafficStats;
45 import android.net.Uri;
46 import android.os.Binder;
47 import android.os.IBinder;
48 import android.os.Process;
49 import android.os.RemoteException;
50 import android.os.StrictMode;
51 import android.os.StrictMode.ThreadPolicy.Builder;
52 import android.os.StrictMode.ViolationInfo;
53 import android.os.strictmode.CleartextNetworkViolation;
54 import android.os.strictmode.CustomViolation;
55 import android.os.strictmode.DiskReadViolation;
56 import android.os.strictmode.DiskWriteViolation;
57 import android.os.strictmode.ExplicitGcViolation;
58 import android.os.strictmode.FileUriExposedViolation;
59 import android.os.strictmode.InstanceCountViolation;
60 import android.os.strictmode.LeakedClosableViolation;
61 import android.os.strictmode.NetworkViolation;
62 import android.os.strictmode.NonSdkApiUsedViolation;
63 import android.os.strictmode.UnbufferedIoViolation;
64 import android.os.strictmode.UnsafeIntentLaunchViolation;
65 import android.os.strictmode.UntaggedSocketViolation;
66 import android.os.strictmode.Violation;
67 import android.platform.test.annotations.AppModeFull;
68 import android.platform.test.annotations.AppModeInstant;
69 import android.platform.test.annotations.AppModeSdkSandbox;
70 import android.platform.test.annotations.Presubmit;
71 import android.system.Os;
72 import android.system.OsConstants;
73 import android.util.Log;
74 import android.view.Display;
75 import android.view.GestureDetector;
76 import android.view.View;
77 import android.view.ViewConfiguration;
78 import android.view.WindowManager;
79 import android.window.WindowProviderService;
80 
81 import androidx.test.core.app.ApplicationProvider;
82 import androidx.test.platform.app.InstrumentationRegistry;
83 import androidx.test.rule.ServiceTestRule;
84 import androidx.test.runner.AndroidJUnit4;
85 
86 import com.android.compatibility.common.util.ApiTest;
87 
88 import org.junit.After;
89 import org.junit.Before;
90 import org.junit.Test;
91 import org.junit.runner.RunWith;
92 
93 import java.io.BufferedOutputStream;
94 import java.io.File;
95 import java.io.FileDescriptor;
96 import java.io.FileInputStream;
97 import java.io.FileNotFoundException;
98 import java.io.FileOutputStream;
99 import java.io.IOException;
100 import java.net.HttpURLConnection;
101 import java.net.Socket;
102 import java.net.URL;
103 import java.util.ArrayList;
104 import java.util.List;
105 import java.util.Random;
106 import java.util.concurrent.ArrayBlockingQueue;
107 import java.util.concurrent.BlockingQueue;
108 import java.util.concurrent.CountDownLatch;
109 import java.util.concurrent.ExecutionException;
110 import java.util.concurrent.Executors;
111 import java.util.concurrent.LinkedBlockingQueue;
112 import java.util.concurrent.TimeUnit;
113 import java.util.function.Consumer;
114 import java.util.zip.GZIPInputStream;
115 import java.util.zip.GZIPOutputStream;
116 
117 /** Tests for {@link StrictMode} */
118 @RunWith(AndroidJUnit4.class)
119 @AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).")
120 public class StrictModeTest {
121     private static final String TAG = "StrictModeTest";
122     private static final String REMOTE_SERVICE_ACTION = "android.app.REMOTESERVICE";
123     private static final String UNSAFE_INTENT_LAUNCH = "UnsafeIntentLaunch";
124 
125     private static final int VIOLATION_TIMEOUT_IN_SECOND = 5;
126     private static final int NO_VIOLATION_TIMEOUT_IN_SECOND = 2;
127 
128     private StrictMode.ThreadPolicy mThreadPolicy;
129     private StrictMode.VmPolicy mVmPolicy;
130 
131     private Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
132     private GestureDetector.OnGestureListener mGestureListener =
133             new GestureDetector.SimpleOnGestureListener();
134     private static final String WM_CLASS_NAME = WindowManager.class.getSimpleName();
135 
getContext()136     private Context getContext() {
137         return ApplicationProvider.getApplicationContext();
138     }
139 
140     @Before
setUp()141     public void setUp() {
142         mThreadPolicy = StrictMode.getThreadPolicy();
143         mVmPolicy = StrictMode.getVmPolicy();
144     }
145 
146     @After
tearDown()147     public void tearDown() {
148         StrictMode.setThreadPolicy(mThreadPolicy);
149         StrictMode.setVmPolicy(mVmPolicy);
150     }
151 
152     public interface ThrowingRunnable {
run()153         void run() throws Exception;
154     }
155 
156     @Test
testThreadBuilder()157     public void testThreadBuilder() throws Exception {
158         StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().detectDiskReads().penaltyLog().build();
159         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder(policy).build());
160 
161         final File test = File.createTempFile("foo", "bar");
162         inspectViolation(
163                 test::exists,
164                 info -> {
165                     assertThat(info.getViolationDetails()).isNull();
166                     assertThat(info.getStackTrace()).contains("DiskReadViolation");
167                 });
168     }
169 
170     @Test
testThreadBuilder_detectUnbufferedIo()171     public void testThreadBuilder_detectUnbufferedIo() throws Exception {
172         StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()
173             .penaltyLog()
174             .detectUnbufferedIo()
175             .build();
176         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder(policy).build());
177 
178         final File test = File.createTempFile("foo", "bar");
179         inspectViolation(
180             () -> {
181                 writeUnbuffered(test);
182             },
183             info -> {
184                 assertThat(info.getViolationDetails()).isNull();
185                 assertThat(info.getStackTrace()).contains("UnbufferedIoViolation");
186             });
187     }
188 
189     @Test
testThreadBuilder_permitUnbufferedIo()190     public void testThreadBuilder_permitUnbufferedIo() throws Exception {
191         StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder()
192             .penaltyLog()
193             .permitUnbufferedIo()
194             .build();
195         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder(policy).build());
196 
197         final File test = File.createTempFile("foo", "bar");
198         inspectViolation(
199             () -> {
200                 writeUnbuffered(test);
201             },
202             info -> {
203                 assertThat(info).isNull();
204             });
205     }
206 
writeUnbuffered(File file)207     private void writeUnbuffered(File file) throws Exception {
208         if (file.exists()) {
209             file.delete();
210         }
211 
212         try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file))) {
213             for (int i = 0; i < 11; i++) {
214                 out.write(1);
215                 out.write(2);
216                 out.write(3);
217                 out.write(4);
218                 out.flush();
219             }
220         } finally {
221             if (file.exists()) {
222                 file.delete();
223             }
224         }
225     }
226 
227     @Test
testUnclosedCloseable()228     public void testUnclosedCloseable() throws Exception {
229         //clean before test
230         System.gc();
231         System.runFinalization();
232 
233         StrictMode.setVmPolicy(
234                 new StrictMode.VmPolicy.Builder().detectLeakedClosableObjects().build());
235 
236         inspectViolation(
237                 () -> leakCloseable("leaked.txt"),
238                 info -> {
239                     assertThat(info.getViolationDetails())
240                             .isEqualTo(
241                                     "A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks.");
242                     assertThat(info.getStackTrace())
243                             .contains("Explicit termination method 'close' not called");
244                     assertThat(info.getStackTrace()).contains("leakCloseable");
245                     assertThat(info.getViolationClass())
246                             .isAssignableTo(LeakedClosableViolation.class);
247                 });
248     }
249 
leakCloseable(String fileName)250     private void leakCloseable(String fileName) throws InterruptedException {
251         final CountDownLatch finalizedSignal = new CountDownLatch(1);
252         try {
253             new FileOutputStream(new File(getContext().getFilesDir(), fileName)) {
254                 @Override
255                 protected void finalize() throws IOException {
256                     super.finalize();
257                     finalizedSignal.countDown();
258                 }
259             };
260         } catch (FileNotFoundException e) {
261             throw new RuntimeException(e);
262         }
263         Runtime.getRuntime().gc();
264         Runtime.getRuntime().runFinalization();
265         // Sometimes it needs extra prodding.
266         if (!finalizedSignal.await(5, TimeUnit.SECONDS)) {
267             Runtime.getRuntime().gc();
268             Runtime.getRuntime().runFinalization();
269         }
270     }
271 
272     @Test
testClassInstanceLimit()273     public void testClassInstanceLimit() throws Exception {
274         StrictMode.setVmPolicy(
275                 new StrictMode.VmPolicy.Builder()
276                         .setClassInstanceLimit(LimitedClass.class, 1)
277                         .build());
278         List<LimitedClass> references = new ArrayList<>();
279         assertNoViolation(() -> references.add(new LimitedClass()));
280         references.add(new LimitedClass());
281         inspectViolation(
282                 StrictMode::conditionallyCheckInstanceCounts,
283                 info -> assertThat(info.getViolationClass())
284                         .isAssignableTo(InstanceCountViolation.class));
285     }
286 
287     private static final class LimitedClass {}
288 
289     /** Insecure connection should be detected */
290     @AppModeFull
291     @Test
testCleartextNetwork()292     public void testCleartextNetwork() throws Exception {
293         if (!hasInternetConnection()) {
294             Log.i(TAG, "testCleartextNetwork() ignored on device without Internet");
295             return;
296         }
297 
298         StrictMode.setVmPolicy(
299                 new StrictMode.VmPolicy.Builder().detectCleartextNetwork().penaltyLog().build());
300 
301         inspectViolation(
302                 () ->
303                         ((HttpURLConnection) new URL("http://example.com/").openConnection())
304                                 .getResponseCode(),
305                 info -> assertThat(info.getViolationClass())
306                         .isAssignableTo(CleartextNetworkViolation.class));
307     }
308 
309     /** Secure connection should be ignored */
310     @Test
testEncryptedNetwork()311     public void testEncryptedNetwork() throws Exception {
312         if (!hasInternetConnection()) {
313             Log.i(TAG, "testEncryptedNetwork() ignored on device without Internet");
314             return;
315         }
316 
317         StrictMode.setVmPolicy(
318                 new StrictMode.VmPolicy.Builder().detectCleartextNetwork().penaltyLog().build());
319 
320         assertNoViolation(
321                 () ->
322                         ((HttpURLConnection) new URL("https://example.com/").openConnection())
323                                 .getResponseCode());
324     }
325 
326     @Test
testFileUriExposure()327     public void testFileUriExposure() throws Exception {
328         StrictMode.setVmPolicy(
329                 new StrictMode.VmPolicy.Builder().detectFileUriExposure().penaltyLog().build());
330 
331         final Uri badUri = Uri.fromFile(new File("/sdcard/meow.jpg"));
332         inspectViolation(
333                 () -> {
334                     Intent intent = new Intent(Intent.ACTION_VIEW);
335                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
336                     intent.setDataAndType(badUri, "image/jpeg");
337                     getContext().startActivity(intent);
338                 },
339                 info -> {
340                     assertThat(info.getStackTrace()).contains(badUri + " exposed beyond app");
341                 });
342 
343         final Uri goodUri = Uri.parse("content://com.example/foobar");
344         assertNoViolation(
345                 () -> {
346                     Intent intent = new Intent(Intent.ACTION_VIEW);
347                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
348                     intent.setDataAndType(goodUri, "image/jpeg");
349                     getContext().startActivity(intent);
350                 });
351     }
352 
353     @Test
testFileUriExposure_Chooser()354     public void testFileUriExposure_Chooser() throws Exception {
355         StrictMode.setVmPolicy(
356                 new StrictMode.VmPolicy.Builder().detectFileUriExposure().penaltyLog().build());
357 
358         final Uri badUri = Uri.fromFile(new File("/sdcard/meow.jpg"));
359         inspectViolation(
360                 () -> {
361                     Intent intent = new Intent(Intent.ACTION_SEND);
362                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
363                     intent.setType("image/jpeg");
364                     intent.putExtra(Intent.EXTRA_STREAM, badUri);
365 
366                     Intent chooser = Intent.createChooser(intent, "CTS");
367                     chooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
368                     getContext().startActivity(chooser);
369                 },
370                 info -> {
371                     assertThat(info.getStackTrace()).contains(badUri + " exposed beyond app");
372                 });
373     }
374 
375     @Test
testContentUriWithoutPermission()376     public void testContentUriWithoutPermission() throws Exception {
377         StrictMode.setVmPolicy(
378                 new StrictMode.VmPolicy.Builder()
379                         .detectContentUriWithoutPermission()
380                         .penaltyLog()
381                         .build());
382 
383         final Uri uri = Uri.parse("content://com.example/foobar");
384         inspectViolation(
385                 () -> {
386                     Intent intent = new Intent(Intent.ACTION_VIEW);
387                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
388                     intent.setDataAndType(uri, "image/jpeg");
389                     getContext().startActivity(intent);
390                 },
391                 info ->
392                         assertThat(info.getStackTrace())
393                                 .contains(uri + " exposed beyond app"));
394 
395         assertNoViolation(
396                 () -> {
397                     Intent intent = new Intent(Intent.ACTION_VIEW);
398                     intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
399                     intent.setDataAndType(uri, "image/jpeg");
400                     intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
401                     getContext().startActivity(intent);
402                 });
403     }
404 
405     @AppModeFull
406     @Test
testUntaggedSocketsHttp()407     public void testUntaggedSocketsHttp() throws Exception {
408         if (!hasInternetConnection()) {
409             Log.i(TAG, "testUntaggedSockets() ignored on device without Internet");
410             return;
411         }
412 
413         StrictMode.setVmPolicy(
414                 new StrictMode.VmPolicy.Builder().detectUntaggedSockets().penaltyLog().build());
415 
416         inspectViolation(
417                 () ->
418                         ((HttpURLConnection) new URL("http://example.com/").openConnection())
419                                 .getResponseCode(),
420                 info -> assertThat(info.getViolationClass())
421                         .isAssignableTo(UntaggedSocketViolation.class));
422 
423         assertNoViolation(
424                 () -> {
425                     TrafficStats.setThreadStatsTag(0xDECAFBAD);
426                     try {
427                         ((HttpURLConnection) new URL("http://example.com/").openConnection())
428                                 .getResponseCode();
429                     } finally {
430                         TrafficStats.clearThreadStatsTag();
431                     }
432                 });
433     }
434 
435     @Test
testUntaggedSocketsRaw()436     public void testUntaggedSocketsRaw() throws Exception {
437         if (!hasInternetConnection()) {
438             Log.i(TAG, "testUntaggedSockets() ignored on device without Internet");
439             return;
440         }
441 
442         StrictMode.setVmPolicy(
443                 new StrictMode.VmPolicy.Builder().detectUntaggedSockets().penaltyLog().build());
444 
445         assertNoViolation(
446                 () -> {
447                     TrafficStats.setThreadStatsTag(0xDECAFBAD);
448                     try (Socket socket = new Socket("example.com", 80)) {
449                         socket.getOutputStream().close();
450                     } finally {
451                         TrafficStats.clearThreadStatsTag();
452                     }
453                 });
454 
455         inspectViolation(
456                 () -> {
457                     try (Socket socket = new Socket("example.com", 80)) {
458                         socket.getOutputStream().close();
459                     }
460                 },
461                 info -> assertThat(info.getViolationClass())
462                         .isAssignableTo(UntaggedSocketViolation.class));
463     }
464 
465     private static final int PERMISSION_USER_ONLY = 0600;
466 
467     @Test
testRead()468     public void testRead() throws Exception {
469         final File test = File.createTempFile("foo", "bar");
470         final File dir = test.getParentFile();
471 
472         final FileInputStream is = new FileInputStream(test);
473         final FileDescriptor fd =
474                 Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, PERMISSION_USER_ONLY);
475 
476         StrictMode.setThreadPolicy(
477                 new StrictMode.ThreadPolicy.Builder().detectDiskReads().penaltyLog().build());
478         inspectViolation(
479                 test::exists,
480                 info -> {
481                     assertThat(info.getViolationDetails()).isNull();
482                     assertThat(info.getStackTrace()).contains("DiskReadViolation");
483                 });
484 
485         Consumer<ViolationInfo> assertDiskReadPolicy = info -> assertThat(
486                 info.getViolationClass()).isAssignableTo(DiskReadViolation.class);
487         inspectViolation(test::exists, assertDiskReadPolicy);
488         inspectViolation(test::length, assertDiskReadPolicy);
489         inspectViolation(dir::list, assertDiskReadPolicy);
490         inspectViolation(is::read, assertDiskReadPolicy);
491 
492         inspectViolation(() -> new FileInputStream(test), assertDiskReadPolicy);
493         inspectViolation(
494                 () -> Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, PERMISSION_USER_ONLY),
495                 assertDiskReadPolicy);
496         inspectViolation(() -> Os.read(fd, new byte[10], 0, 1), assertDiskReadPolicy);
497     }
498 
499     @Test
testWrite()500     public void testWrite() throws Exception {
501         File file = File.createTempFile("foo", "bar");
502 
503         final FileOutputStream os = new FileOutputStream(file);
504         final FileDescriptor fd =
505                 Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, PERMISSION_USER_ONLY);
506 
507         StrictMode.setThreadPolicy(
508                 new StrictMode.ThreadPolicy.Builder().detectDiskWrites().penaltyLog().build());
509 
510         inspectViolation(
511                 file::createNewFile,
512                 info -> {
513                     assertThat(info.getViolationDetails()).isNull();
514                     assertThat(info.getStackTrace()).contains("DiskWriteViolation");
515                 });
516 
517         Consumer<ViolationInfo> assertDiskWritePolicy = info -> assertThat(
518                 info.getViolationClass()).isAssignableTo(DiskWriteViolation.class);
519 
520         inspectViolation(() -> File.createTempFile("foo", "bar"), assertDiskWritePolicy);
521         inspectViolation(() -> new FileOutputStream(file), assertDiskWritePolicy);
522         inspectViolation(file::delete, assertDiskWritePolicy);
523         inspectViolation(file::createNewFile, assertDiskWritePolicy);
524         inspectViolation(() -> os.write(32), assertDiskWritePolicy);
525 
526         inspectViolation(
527                 () -> Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, PERMISSION_USER_ONLY),
528                 assertDiskWritePolicy);
529         inspectViolation(() -> Os.write(fd, new byte[10], 0, 1), assertDiskWritePolicy);
530         inspectViolation(() -> Os.fsync(fd), assertDiskWritePolicy);
531         inspectViolation(
532                 () -> file.renameTo(new File(file.getParent(), "foobar")), assertDiskWritePolicy);
533     }
534 
535     @AppModeFull
536     @Test
testNetwork()537     public void testNetwork() throws Exception {
538         if (!hasInternetConnection()) {
539             Log.i(TAG, "testUntaggedSockets() ignored on device without Internet");
540             return;
541         }
542 
543         StrictMode.setThreadPolicy(
544                 new StrictMode.ThreadPolicy.Builder().detectNetwork().penaltyLog().build());
545 
546         inspectViolation(
547                 () -> {
548                     try (Socket socket = new Socket("example.com", 80)) {
549                         socket.getOutputStream().close();
550                     }
551                 },
552                 info -> assertThat(info.getViolationClass())
553                         .isAssignableTo(NetworkViolation.class));
554         inspectViolation(
555                 () ->
556                         ((HttpURLConnection) new URL("http://example.com/").openConnection())
557                                 .getResponseCode(),
558                 info -> assertThat(info.getViolationClass())
559                         .isAssignableTo(NetworkViolation.class));
560     }
561 
562     @Test
testExplicitGc()563     public void testExplicitGc() throws Exception {
564         StrictMode.setThreadPolicy(
565                 new StrictMode.ThreadPolicy.Builder().detectExplicitGc().penaltyLog().build());
566 
567         inspectViolation(
568                 () -> { Runtime.getRuntime().gc(); },
569                 info -> assertThat(info.getViolationClass())
570                         .isAssignableTo(ExplicitGcViolation.class));
571     }
572 
573     @Test
574     @ApiTest(apis={"StrictMode.VmPolicy.Builder#permitExplicitGc"})
testPermitExplicitGc()575     public void testPermitExplicitGc() throws Exception {
576         StrictMode.setThreadPolicy(
577                 new StrictMode.ThreadPolicy.Builder().permitExplicitGc().penaltyLog().build());
578 
579         assertNoViolation(() -> Runtime.getRuntime().gc());
580     }
581 
582     @Test
583     @ApiTest(apis={"StrictMode.VmPolicy.Builder#detectUnbufferedIo"})
testUnbufferedIoGZipInput()584     public void testUnbufferedIoGZipInput() throws Exception {
585         StrictMode.setThreadPolicy(
586                 new StrictMode.ThreadPolicy.Builder().detectUnbufferedIo().penaltyLog().build());
587 
588         inspectViolation(
589                 () -> {
590                     File tmp = File.createTempFile("StrictModeTest", "tmp");
591                     try (FileOutputStream fos = new FileOutputStream(tmp);
592                          GZIPOutputStream gzippedOut = new GZIPOutputStream(fos)) {
593                         byte[] data = new byte[10240];
594                         new Random().nextBytes(data);
595                         gzippedOut.write(data);
596                     }
597 
598                     try (FileInputStream fileInputStream = new FileInputStream(tmp);
599                         GZIPInputStream in = new GZIPInputStream(fileInputStream)) {
600 
601                         byte[] buffer = new byte[1024];
602                         while (in.read(buffer) != -1) {}
603                     }
604                 },
605                 info -> assertThat(info.getViolationClass())
606                         .isAssignableTo(UnbufferedIoViolation.class));
607     }
608 
609     @Test
testUnbufferedIoGZipOutput()610     public void testUnbufferedIoGZipOutput() throws Exception {
611         StrictMode.setThreadPolicy(
612                 new StrictMode.ThreadPolicy.Builder().detectUnbufferedIo().penaltyLog().build());
613 
614         inspectViolation(
615                 () -> {
616                     byte[] data = new byte[512];
617                     Random random = new Random(0);
618                     try (FileOutputStream ostream = new FileOutputStream(
619                             File.createTempFile("StrictModeTest","testUnbufferedIo.dat"));
620                         GZIPOutputStream gzippedOut = new GZIPOutputStream(ostream)) {
621                         for (int i = 0; i < 9; i++) {
622                             random.nextBytes(data);
623                             gzippedOut.write(data, 0, data.length);
624                         }
625                     }
626                 },
627                 info -> assertThat(info.getViolationClass())
628                         .isAssignableTo(UnbufferedIoViolation.class));
629     }
630 
631 
632     @Test
testViolationAcrossBinder()633     public void testViolationAcrossBinder() throws Exception {
634         runWithRemoteServiceBound(
635                 getContext(),
636                 service -> {
637                     StrictMode.setThreadPolicy(
638                             new Builder().detectDiskWrites().penaltyLog().build());
639 
640                     try {
641                         inspectViolation(
642                                 () -> service.performDiskWrite(),
643                                 (info) -> {
644                                     assertThat(info.getViolationClass())
645                                             .isAssignableTo(DiskWriteViolation.class);
646                                     assertThat(info.getViolationDetails())
647                                             .isNull(); // Disk write has no message.
648                                     assertThat(info.getStackTrace())
649                                             .contains("DiskWriteViolation");
650                                     assertThat(info.getStackTrace())
651                                             .contains(
652                                                     "at android.os.StrictMode$AndroidBlockGuardPolicy.onWriteToDisk");
653                                     assertThat(info.getStackTrace())
654                                             .contains("# via Binder call with stack:");
655                                     assertThat(info.getStackTrace())
656                                             .contains(
657                                                     "at android.os.cts.ISecondary$Stub$Proxy.performDiskWrite");
658                                 });
659                         assertNoViolation(() -> service.getPid());
660                     } catch (Exception e) {
661                         throw new RuntimeException(e);
662                     }
663                 });
664     }
665 
checkNonSdkApiUsageViolation(boolean blacklist, String className, String methodName, Class<?>... paramTypes)666     private void checkNonSdkApiUsageViolation(boolean blacklist, String className,
667             String methodName, Class<?>... paramTypes) throws Exception {
668         Class<?> clazz = Class.forName(className);
669         inspectViolation(
670             () -> {
671                 try {
672                     java.lang.reflect.Method m = clazz.getDeclaredMethod(methodName, paramTypes);
673                     if (blacklist) {
674                         fail();
675                     }
676                 } catch (NoSuchMethodException expected) {
677                   if (!blacklist) {
678                     fail();
679                   }
680                 }
681             },
682             info -> {
683                 assertThat(info).isNotNull();
684                 assertThat(info.getViolationClass())
685                         .isAssignableTo(NonSdkApiUsedViolation.class);
686                 assertThat(info.getViolationDetails()).contains(methodName);
687                 assertThat(info.getStackTrace()).contains("checkNonSdkApiUsageViolation");
688             }
689         );
690     }
691 
692     @SuppressWarnings("ReturnValueIgnored")
693     @Test
testNonSdkApiUsage()694     public void testNonSdkApiUsage() throws Exception {
695         StrictMode.VmPolicy oldVmPolicy = StrictMode.getVmPolicy();
696         StrictMode.ThreadPolicy oldThreadPolicy = StrictMode.getThreadPolicy();
697         try {
698             StrictMode.setVmPolicy(
699                     new StrictMode.VmPolicy.Builder().detectNonSdkApiUsage().build());
700             checkNonSdkApiUsageViolation(
701                 true, "dalvik.system.VMRuntime", "setHiddenApiExemptions", String[].class);
702             // verify that mutliple uses of a light greylist API are detected.
703             checkNonSdkApiUsageViolation(false, "dalvik.system.VMRuntime", "getRuntime");
704             checkNonSdkApiUsageViolation(false, "dalvik.system.VMRuntime", "getRuntime");
705 
706             // Verify that the VM policy is turned off after a call to permitNonSdkApiUsage.
707             StrictMode.setVmPolicy(
708                 new StrictMode.VmPolicy.Builder().permitNonSdkApiUsage().build());
709             assertNoViolation(() -> {
710                   Class<?> clazz = Class.forName("dalvik.system.VMRuntime");
711                   try {
712                       clazz.getDeclaredMethod("getRuntime");
713                   } catch (NoSuchMethodException maybe) {
714                   }
715             });
716         } finally {
717             StrictMode.setVmPolicy(oldVmPolicy);
718             StrictMode.setThreadPolicy(oldThreadPolicy);
719         }
720     }
721 
722     @Test
testThreadPenaltyListener()723     public void testThreadPenaltyListener() throws Exception {
724         final BlockingQueue<Violation> violations = new ArrayBlockingQueue<>(1);
725         StrictMode.setThreadPolicy(
726                 new StrictMode.ThreadPolicy.Builder().detectCustomSlowCalls()
727                         .penaltyListener(getContext().getMainExecutor(), (v) -> {
728                             violations.add(v);
729                         }).build());
730 
731         StrictMode.noteSlowCall("foo");
732 
733         final Violation v = violations.poll(5, TimeUnit.SECONDS);
734         assertTrue(v instanceof CustomViolation);
735     }
736 
737     @Test
testVmPenaltyListener()738     public void testVmPenaltyListener() throws Exception {
739         final BlockingQueue<Violation> violations = new ArrayBlockingQueue<>(1);
740         StrictMode.setVmPolicy(
741                 new StrictMode.VmPolicy.Builder().detectFileUriExposure()
742                         .penaltyListener(getContext().getMainExecutor(), (v) -> {
743                             violations.add(v);
744                         }).build());
745 
746         Intent intent = new Intent(Intent.ACTION_VIEW);
747         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
748         intent.setDataAndType(Uri.fromFile(new File("/sdcard/meow.jpg")), "image/jpeg");
749         getContext().startActivity(intent);
750 
751         final Violation v = violations.poll(5, TimeUnit.SECONDS);
752         assertTrue(v instanceof FileUriExposedViolation);
753     }
754 
755     @AppModeInstant
756     @Test
testNoCleartextHttpTrafficAllowed()757     public void testNoCleartextHttpTrafficAllowed() throws Exception {
758         if (!hasInternetConnection()) {
759             Log.i(TAG, "testNoCleartextHttpTrafficAllowed() ignored on device without Internet");
760             return;
761         }
762 
763         StrictMode.setVmPolicy(
764                 new StrictMode.VmPolicy.Builder().detectCleartextNetwork().penaltyLog().build());
765 
766         try {
767             inspectViolation(
768                     () ->
769                             ((HttpURLConnection) new URL("http://example.com/").openConnection())
770                                     .getResponseCode(),
771                     info -> assertThat(info.getViolationClass())
772                             .isAssignableTo(CleartextNetworkViolation.class));
773             fail("Instant app was able to send cleartext http traffic.");
774         } catch (IOException ex) {
775             // Expected
776         }
777     }
778 
779     @Presubmit
780     @Test
testIncorrectContextUse_Application_ThrowViolation()781     public void testIncorrectContextUse_Application_ThrowViolation() throws Exception {
782         StrictMode.setVmPolicy(
783                 new StrictMode.VmPolicy.Builder()
784                         .detectIncorrectContextUse()
785                         .penaltyLog()
786                         .build());
787 
788         final Context applicationContext = getContext();
789 
790         assertViolation("Tried to access visual service " + WM_CLASS_NAME,
791                 () -> applicationContext.getSystemService(WindowManager.class));
792 
793         assertViolation(
794                 "The API:ViewConfiguration needs a proper configuration.",
795                 () -> ViewConfiguration.get(applicationContext));
796 
797         mInstrumentation.runOnMainSync(() -> {
798             try {
799                 assertViolation("The API:GestureDetector#init needs a proper configuration.",
800                         () -> new GestureDetector(applicationContext, mGestureListener));
801             } catch (Exception e) {
802                 fail("Failed because of " + e);
803             }
804         });
805 
806         if (isWallpaperSupported()) {
807             assertViolation("Tried to access UI related API:", () ->
808                     applicationContext.getSystemService(WallpaperManager.class)
809                             .getDesiredMinimumWidth());
810         }
811     }
812 
813     @Presubmit
814     @Test
testIncorrectContextUse_DisplayContext_ThrowViolation()815     public void testIncorrectContextUse_DisplayContext_ThrowViolation() throws Exception {
816         StrictMode.setVmPolicy(
817                 new StrictMode.VmPolicy.Builder()
818                         .detectIncorrectContextUse()
819                         .penaltyLog()
820                         .build());
821 
822         final Display display = getContext().getSystemService(DisplayManager.class)
823                 .getDisplay(DEFAULT_DISPLAY);
824         final Context displayContext = getContext().createDisplayContext(display);
825 
826         assertViolation("Tried to access visual service " + WM_CLASS_NAME,
827                 () -> displayContext.getSystemService(WindowManager.class));
828 
829         assertViolation(
830                 "The API:ViewConfiguration needs a proper configuration.",
831                 () -> ViewConfiguration.get(displayContext));
832 
833         mInstrumentation.runOnMainSync(() -> {
834             try {
835                 assertViolation("The API:GestureDetector#init needs a proper configuration.",
836                         () -> new GestureDetector(displayContext, mGestureListener));
837             } catch (Exception e) {
838                 fail("Failed because of " + e);
839             }
840         });
841 
842         if (isWallpaperSupported()) {
843             assertViolation("Tried to access UI related API:", () ->
844                     displayContext.getSystemService(WallpaperManager.class)
845                             .getDesiredMinimumWidth());
846         }
847     }
848 
849     @Presubmit
850     @ApiTest(apis = {
851             "android.content.Context#createWindowContext",
852             "android.content.Context#getSystemService",
853             "android.view.ViewConfiguration#get",
854             "android.view.GestureDetector#GestureDetector",
855             "android.app.WallpaperManager#getDesiredMinimumWidth",
856     })
857     @Test
testIncorrectContextUse_WindowContext_NoViolation()858     public void testIncorrectContextUse_WindowContext_NoViolation() {
859         StrictMode.setVmPolicy(
860                 new StrictMode.VmPolicy.Builder()
861                         .detectIncorrectContextUse()
862                         .penaltyLog()
863                         .build());
864 
865         final Context windowContext = createWindowContext();
866 
867         mInstrumentation.runOnMainSync(() -> {
868             try {
869                 assertNoViolation(() -> {
870                     windowContext.getSystemService(WINDOW_SERVICE);
871                     ViewConfiguration.get(windowContext);
872                     new GestureDetector(windowContext, mGestureListener);
873                     if (isWallpaperSupported()) {
874                         windowContext.getSystemService(WallpaperManager.class)
875                                 .getDesiredMinimumWidth();
876                     }
877                 });
878             } catch (Exception e) {
879                 fail("Failed because of " + e);
880             }
881         });
882     }
883 
884     @Presubmit
885     @ApiTest(apis = {
886             "android.app.Activity#getSystemService",
887             "android.content.Context#getSystemService",
888             "android.view.ViewConfiguration#get",
889             "android.view.GestureDetector#GestureDetector",
890             "android.app.WallpaperManager#getDesiredMinimumWidth",
891     })
892     @Test
testIncorrectContextUse_Activity_NoViolation()893     public void testIncorrectContextUse_Activity_NoViolation() {
894         StrictMode.setVmPolicy(
895                 new StrictMode.VmPolicy.Builder()
896                         .detectIncorrectContextUse()
897                         .penaltyLog()
898                         .build());
899 
900         Intent intent = new Intent(getContext(), SimpleTestActivity.class);
901         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
902         final Activity activity = mInstrumentation.startActivitySync(intent);
903 
904         mInstrumentation.runOnMainSync(() -> {
905             try {
906                 assertNoViolation(() -> {
907                     activity.getSystemService(WINDOW_SERVICE);
908                     ViewConfiguration.get(activity);
909                     new GestureDetector(activity, mGestureListener);
910                     if (isWallpaperSupported()) {
911                         activity.getSystemService(WallpaperManager.class)
912                                 .getDesiredMinimumWidth();
913                     }
914                 });
915             } catch (Exception e) {
916                 fail("Failed because of " + e);
917             }
918         });
919     }
920 
921     @Presubmit
922     @ApiTest(apis = {
923             "android.content.Context#createWindowContext",
924             "android.content.Context#createConfigurationContext",
925             "android.content.Context#createAttributionContext",
926             "android.content.Context#getSystemService",
927             "android.view.ViewConfiguration#get",
928             "android.view.GestureDetector#GestureDetector",
929             "android.app.WallpaperManager#getDesiredMinimumWidth",
930     })
931     @Test
testIncorrectContextUse_UiDerivedContext_NoViolation()932     public void testIncorrectContextUse_UiDerivedContext_NoViolation() {
933         StrictMode.setVmPolicy(
934                 new StrictMode.VmPolicy.Builder()
935                         .detectIncorrectContextUse()
936                         .penaltyLog()
937                         .build());
938 
939         final Configuration configuration = new Configuration();
940         configuration.setToDefaults();
941         final Context uiDerivedConfigContext =
942                 createWindowContext().createConfigurationContext(configuration);
943 
944         mInstrumentation.runOnMainSync(() -> {
945             try {
946                 assertNoViolation(() -> {
947                     uiDerivedConfigContext.getSystemService(WINDOW_SERVICE);
948                     ViewConfiguration.get(uiDerivedConfigContext);
949                     new GestureDetector(uiDerivedConfigContext, mGestureListener);
950                     if (isWallpaperSupported()) {
951                         uiDerivedConfigContext.getSystemService(WallpaperManager.class)
952                                 .getDesiredMinimumWidth();
953                     }
954                 });
955             } catch (Exception e) {
956                 fail("Failed because of " + e);
957             }
958         });
959 
960         final Context uiDerivedAttrContext = createWindowContext()
961                 .createAttributionContext(null /* attributeTag */);
962 
963         mInstrumentation.runOnMainSync(() -> {
964             try {
965                 assertNoViolation(() -> {
966                     uiDerivedAttrContext.getSystemService(WINDOW_SERVICE);
967                     ViewConfiguration.get(uiDerivedAttrContext);
968                     new GestureDetector(uiDerivedAttrContext, mGestureListener);
969                     if (isWallpaperSupported()) {
970                         uiDerivedAttrContext.getSystemService(WallpaperManager.class)
971                                 .getDesiredMinimumWidth();
972                     }
973                 });
974             } catch (Exception e) {
975                 fail("Failed because of " + e);
976             }
977         });
978     }
979 
980     @Presubmit
981     @ApiTest(apis = {
982             "android.content.Context#createWindowContext",
983             "android.content.Context#createDisplayContext",
984             "android.content.Context#getSystemService",
985             "android.view.ViewConfiguration#get",
986             "android.view.GestureDetector#GestureDetector",
987             "android.app.WallpaperManager#getDesiredMinimumWidth",
988     })
989     @Test
testIncorrectContextUse_UiDerivedDisplayContext_ThrowViolation()990     public void testIncorrectContextUse_UiDerivedDisplayContext_ThrowViolation() throws Exception {
991         StrictMode.setVmPolicy(
992                 new StrictMode.VmPolicy.Builder()
993                         .detectIncorrectContextUse()
994                         .penaltyLog()
995                         .build());
996 
997         final Display display = getContext().getSystemService(DisplayManager.class)
998                 .getDisplay(DEFAULT_DISPLAY);
999         final Context uiDerivedDisplayContext = createWindowContext().createDisplayContext(display);
1000 
1001         assertViolation("Tried to access visual service " + WM_CLASS_NAME,
1002                 () -> uiDerivedDisplayContext.getSystemService(WindowManager.class));
1003 
1004         assertViolation(
1005                 "The API:ViewConfiguration needs a proper configuration.",
1006                 () -> ViewConfiguration.get(uiDerivedDisplayContext));
1007 
1008         mInstrumentation.runOnMainSync(() -> {
1009             try {
1010                 assertViolation("The API:GestureDetector#init needs a proper configuration.",
1011                         () -> new GestureDetector(uiDerivedDisplayContext, mGestureListener));
1012             } catch (Exception e) {
1013                 fail("Failed because of " + e);
1014             }
1015         });
1016 
1017         if (isWallpaperSupported()) {
1018             assertViolation("Tried to access UI related API:", () ->
1019                     uiDerivedDisplayContext.getSystemService(WallpaperManager.class)
1020                             .getDesiredMinimumWidth());
1021         }
1022     }
1023 
1024     @Presubmit
1025     @ApiTest(apis = {
1026             "android.content.Context#createConfigurationContext",
1027             "android.content.Context#getSystemService",
1028             "android.view.ViewConfiguration#get",
1029             "android.view.GestureDetector#GestureDetector",
1030             "android.app.WallpaperManager#getDesiredMinimumWidth",
1031     })
1032     @Test
testIncorrectContextUse_ConfigContext()1033     public void testIncorrectContextUse_ConfigContext() throws Exception {
1034         StrictMode.setVmPolicy(
1035                 new StrictMode.VmPolicy.Builder()
1036                         .detectIncorrectContextUse()
1037                         .penaltyLog()
1038                         .build());
1039 
1040         final Configuration configuration = new Configuration();
1041         configuration.setToDefaults();
1042         final Context configContext = getContext().createConfigurationContext(configuration);
1043 
1044         assertViolation("Tried to access visual service " + WM_CLASS_NAME,
1045                 () -> configContext.getSystemService(WindowManager.class));
1046 
1047         // Make the ViewConfiguration to be cached so that we won't call WindowManager
1048         ViewConfiguration.get(configContext);
1049 
1050         mInstrumentation.runOnMainSync(() -> {
1051             try {
1052                 assertNoViolation(() -> {
1053                     ViewConfiguration.get(configContext);
1054                     new GestureDetector(configContext, mGestureListener);
1055                 });
1056             } catch (Exception e) {
1057                 fail("Failed because of " + e);
1058             }
1059         });
1060 
1061         if (isWallpaperSupported()) {
1062             assertViolation("Tried to access UI related API:", () ->
1063                     configContext.getSystemService(WallpaperManager.class)
1064                             .getDesiredMinimumWidth());
1065         }
1066     }
1067 
1068     @Presubmit
1069     @ApiTest(apis = {
1070             "android.content.Context#createConfigurationContext",
1071             "android.content.Context#createDisplayContext",
1072             "android.content.Context#getSystemService",
1073             "android.view.ViewConfiguration#get",
1074             "android.view.GestureDetector#GestureDetector",
1075             "android.app.WallpaperManager#getDesiredMinimumWidth",
1076     })
1077     @Test
testIncorrectContextUse_ConfigDerivedDisplayContext()1078     public void testIncorrectContextUse_ConfigDerivedDisplayContext() throws Exception {
1079         StrictMode.setVmPolicy(
1080                 new StrictMode.VmPolicy.Builder()
1081                         .detectIncorrectContextUse()
1082                         .penaltyLog()
1083                         .build());
1084 
1085         final Display display = getContext().getSystemService(DisplayManager.class)
1086                 .getDisplay(DEFAULT_DISPLAY);
1087         final Configuration configuration = new Configuration();
1088         configuration.setToDefaults();
1089         final Context configDerivedDisplayContext = getContext()
1090                 .createConfigurationContext(configuration).createDisplayContext(display);
1091 
1092         assertViolation("Tried to access visual service " + WM_CLASS_NAME,
1093                 () -> configDerivedDisplayContext.getSystemService(WindowManager.class));
1094 
1095         assertViolation(
1096                 "The API:ViewConfiguration needs a proper configuration.",
1097                 () -> ViewConfiguration.get(configDerivedDisplayContext));
1098 
1099         mInstrumentation.runOnMainSync(() -> {
1100             try {
1101                 assertViolation("The API:GestureDetector#init needs a proper configuration.",
1102                         () -> new GestureDetector(configDerivedDisplayContext, mGestureListener));
1103             } catch (Exception e) {
1104                 fail("Failed because of " + e);
1105             }
1106         });
1107 
1108         if (isWallpaperSupported()) {
1109             assertViolation("Tried to access UI related API:", () ->
1110                     configDerivedDisplayContext.getSystemService(WallpaperManager.class)
1111                             .getDesiredMinimumWidth());
1112         }
1113     }
1114 
1115     @Presubmit
1116     @ApiTest(apis = {
1117             "android.app.Service#getSystemService",
1118             "android.content.Context#getSystemService",
1119             "android.view.ViewConfiguration#get",
1120             "android.view.GestureDetector#GestureDetector",
1121             "android.app.WallpaperManager#getDesiredMinimumWidth",
1122     })
1123     @Test
testIncorrectContextUse_Service_ThrowViolation()1124     public void testIncorrectContextUse_Service_ThrowViolation() throws Exception {
1125         StrictMode.setVmPolicy(
1126                 new StrictMode.VmPolicy.Builder()
1127                         .detectIncorrectContextUse()
1128                         .penaltyLog()
1129                         .build());
1130 
1131         final Intent intent = new Intent(getContext(), TestService.class);
1132         final ServiceTestRule serviceRule = new ServiceTestRule();
1133         TestService service = ((TestService.TestToken) serviceRule.bindService(intent))
1134                 .getService();
1135         try {
1136             assertViolation("Tried to access visual service " + WM_CLASS_NAME,
1137                     () -> service.getSystemService(WindowManager.class));
1138 
1139             assertViolation(
1140                     "The API:ViewConfiguration needs a proper configuration.",
1141                     () -> ViewConfiguration.get(service));
1142 
1143             mInstrumentation.runOnMainSync(() -> {
1144                 try {
1145                     assertViolation("The API:GestureDetector#init needs a proper configuration.",
1146                             () -> new GestureDetector(service,
1147                                     mGestureListener));
1148                 } catch (Exception e) {
1149                     fail("Failed because of " + e);
1150                 }
1151             });
1152 
1153             if (isWallpaperSupported()) {
1154                 assertViolation("Tried to access UI related API:", () ->
1155                         service.getSystemService(WallpaperManager.class)
1156                                 .getDesiredMinimumWidth());
1157             }
1158         } finally {
1159             serviceRule.unbindService();
1160         }
1161     }
1162 
1163     @Presubmit
1164     @ApiTest(apis = {
1165             "android.window.WindowProviderService#getSystemService",
1166             "android.content.Context#getSystemService",
1167             "android.view.ViewConfiguration#get",
1168             "android.view.GestureDetector#GestureDetector",
1169             "android.app.WallpaperManager#getDesiredMinimumWidth",
1170             "android.view.WindowManager#addView",
1171     })
1172     @Test
testIncorrectContextUse_WindowProviderService_NoViolation()1173     public void testIncorrectContextUse_WindowProviderService_NoViolation() throws Exception {
1174         StrictMode.setVmPolicy(
1175                 new StrictMode.VmPolicy.Builder()
1176                         .detectIncorrectContextUse()
1177                         .penaltyLog()
1178                         .build());
1179 
1180         final Intent intent = new Intent(getContext(), TestWindowService.class);
1181         final ServiceTestRule serviceRule = new ServiceTestRule();
1182         TestWindowService service = ((TestWindowService.TestToken) serviceRule.bindService(intent))
1183                 .getService();
1184         try {
1185             final View view = new View(service);
1186             final WindowManager.LayoutParams correctType =
1187                     new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
1188             final WindowManager.LayoutParams wrongType =
1189                     new WindowManager.LayoutParams(TYPE_PHONE);
1190             final WindowManager[] wm = new WindowManager[1];
1191 
1192             mInstrumentation.runOnMainSync(() -> {
1193                 try {
1194                     // Hold INTERNAL_SYSTEM_WINDOW and SYSTEM_ALERT_WINDOW permission to
1195                     // add TYPE_APPLICATION_OVERLAY and TYPE_PHONE window.
1196                     mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(
1197                             INTERNAL_SYSTEM_WINDOW, SYSTEM_ALERT_WINDOW);
1198 
1199                     assertNoViolation(() -> {
1200                         ViewConfiguration.get(service);
1201                         new GestureDetector(service, mGestureListener);
1202                         if (isWallpaperSupported()) {
1203                             service.getSystemService(WallpaperManager.class)
1204                                     .getDesiredMinimumWidth();
1205                         }
1206                         wm[0] = service.getSystemService(WindowManager.class);
1207                         wm[0].addView(view, correctType);
1208                     });
1209                     wm[0].removeViewImmediate(view);
1210 
1211                     assertViolation("WindowContext's window type must match type in "
1212                             + "WindowManager.LayoutParams", () -> wm[0].addView(view, wrongType));
1213                 } catch (Exception e) {
1214                     fail("Failed because of " + e);
1215                 } finally {
1216                     mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
1217                 }
1218             });
1219         } finally {
1220             serviceRule.unbindService();
1221         }
1222     }
1223 
1224     /**
1225      * Returns {@code true} to indicate that wallpaper is supported.
1226      * <p>
1227      * Note that we check the nullity of {@link WallpaperManager} because it may not be obtainable
1228      * if the test is targeting at least {@link android.os.Build.VERSION_CODES#P} and running in
1229      * instant mode.
1230      */
isWallpaperSupported()1231     private boolean isWallpaperSupported() {
1232         if (Process.isSdkSandbox()) {
1233             // SDK Sandbox does not have access to WallpaperManager.
1234             return false;
1235         }
1236         final WallpaperManager wallpaperManager = WallpaperManager.getInstance(getContext());
1237         return wallpaperManager != null && wallpaperManager.isWallpaperSupported();
1238     }
1239 
1240     @Test
testUnsafeIntentLaunch_ParceledIntentToActivity_ThrowsViolation()1241     public void testUnsafeIntentLaunch_ParceledIntentToActivity_ThrowsViolation() throws Exception {
1242         // The UnsafeIntentLaunch StrictMode check is intended to detect and report unparceling and
1243         // launching of Intents from the delivered Intent. This test verifies a violation is
1244         // reported when an inner Intent is unparceled from the Intent delivered to an Activity and
1245         // used to start another Activity. This test also uses its own OnVmViolationListener to
1246         // obtain the actual StrictMode Violation to verify the getIntent method of the
1247         // UnsafeIntentLaunchViolation returns the Intent that triggered the Violation.
1248         final LinkedBlockingQueue<Violation> violations = new LinkedBlockingQueue<>();
1249         StrictMode.setVmPolicy(
1250                 new StrictMode.VmPolicy.Builder()
1251                         .detectUnsafeIntentLaunch()
1252                         .penaltyListener(Executors.newSingleThreadExecutor(),
1253                                 violation -> violations.add(violation))
1254                         .build());
1255         Context context = getContext();
1256         Intent intent = IntentLaunchActivity.getUnsafeIntentLaunchTestIntent(context);
1257         Intent innerIntent = intent.getParcelableExtra(IntentLaunchActivity.EXTRA_INNER_INTENT);
1258 
1259         context.startActivity(intent);
1260         Violation violation = violations.poll(5, TimeUnit.SECONDS);
1261         assertThat(violation).isInstanceOf(UnsafeIntentLaunchViolation.class);
1262         // The inner Intent will only have the target component set; since the Intent references
1263         // may not be the same compare the component of the Intent that triggered the violation
1264         // against the inner Intent obtained above.
1265         assertThat(((UnsafeIntentLaunchViolation) violation).getIntent().getComponent()).isEqualTo(
1266                 innerIntent.getComponent());
1267     }
1268 
1269     @Test
testUnsafeIntentLaunch_ParceledIntentToActivityCheckDisabled_NoViolation()1270     public void testUnsafeIntentLaunch_ParceledIntentToActivityCheckDisabled_NoViolation()
1271             throws Exception {
1272         // This test verifies the StrictMode violation is not reported when unsafe intent launching
1273         // is permitted through the VmPolicy Builder permit API.
1274         StrictMode.setVmPolicy(
1275                 new StrictMode.VmPolicy.Builder()
1276                         .permitUnsafeIntentLaunch()
1277                         .penaltyLog()
1278                         .build());
1279         Context context = getContext();
1280         Intent intent = IntentLaunchActivity.getUnsafeIntentLaunchTestIntent(context);
1281 
1282         assertNoViolation(() -> context.startActivity(intent));
1283     }
1284 
1285     @Test
testUnsafeIntentLaunch_ParceledIntentToBoundService_ThrowsViolation()1286     public void testUnsafeIntentLaunch_ParceledIntentToBoundService_ThrowsViolation()
1287             throws Exception {
1288         // This test verifies a violation is reported when an inner Intent is unparceled from the
1289         // Intent delivered to a bound Service and used to bind to another service.
1290         StrictMode.setVmPolicy(
1291                 new StrictMode.VmPolicy.Builder()
1292                         .detectUnsafeIntentLaunch()
1293                         .penaltyLog()
1294                         .build());
1295         Context context = getContext();
1296         Intent intent = IntentLaunchService.getTestIntent(context);
1297 
1298         assertViolation(UNSAFE_INTENT_LAUNCH,
1299                 () -> context.bindService(intent, IntentLaunchService.getServiceConnection(),
1300                         Context.BIND_AUTO_CREATE));
1301     }
1302 
1303     @Test
testUnsafeIntentLaunch_ParceledIntentToStartedService_ThrowsViolation()1304     public void testUnsafeIntentLaunch_ParceledIntentToStartedService_ThrowsViolation()
1305             throws Exception {
1306         // This test verifies a violation is reported when an inner Intent is unparceled from the
1307         // Intent delivered to a started Service and used to start another service.
1308         StrictMode.setVmPolicy(
1309                 new StrictMode.VmPolicy.Builder()
1310                         .detectUnsafeIntentLaunch()
1311                         .penaltyLog()
1312                         .build());
1313         Context context = getContext();
1314         Intent intent = IntentLaunchService.getTestIntent(context);
1315 
1316         assertViolation(UNSAFE_INTENT_LAUNCH, () -> context.startService(intent));
1317     }
1318 
1319     @Test
1320     @AppModeFull(reason = "Instant apps can only declare runtime receivers")
testUnsafeIntentLaunch_ParceledIntentToStaticReceiver_ThrowsViolation()1321     public void testUnsafeIntentLaunch_ParceledIntentToStaticReceiver_ThrowsViolation()
1322             throws Exception {
1323         // This test verifies a violation is reported when an inner Intent is unparceled from the
1324         // Intent delivered to a statically declared BroadcastReceiver and used to send another
1325         // broadcast.
1326         StrictMode.setVmPolicy(
1327                 new StrictMode.VmPolicy.Builder()
1328                         .detectUnsafeIntentLaunch()
1329                         .penaltyLog()
1330                         .build());
1331         Context context = getContext();
1332         Intent intent = new Intent(context, IntentLaunchReceiver.class);
1333         Intent innerIntent = new Intent("android.os.cts.TEST_BROADCAST_ACTION");
1334         intent.putExtra(IntentLaunchReceiver.INNER_INTENT_KEY, innerIntent);
1335 
1336         assertViolation(UNSAFE_INTENT_LAUNCH, () -> context.sendBroadcast(intent));
1337     }
1338 
1339     @Test
testUnsafeIntentLaunch_ParceledIntentToDynamicReceiver_ThrowsViolation()1340     public void testUnsafeIntentLaunch_ParceledIntentToDynamicReceiver_ThrowsViolation()
1341             throws Exception {
1342         // This test verifies a violation is reported when an inner Intent is unparceled from the
1343         // Intent delivered to a dynamically registered BroadcastReceiver and used to send another
1344         // broadcast.
1345         StrictMode.setVmPolicy(
1346                 new StrictMode.VmPolicy.Builder()
1347                         .detectUnsafeIntentLaunch()
1348                         .penaltyLog()
1349                         .build());
1350         Context context = getContext();
1351         String receiverAction = "android.os.cts.TEST_INTENT_LAUNCH_RECEIVER_ACTION";
1352         context.registerReceiver(new IntentLaunchReceiver(), new IntentFilter(receiverAction),
1353                 Context.RECEIVER_EXPORTED_UNAUDITED);
1354         Intent intent = new Intent(receiverAction);
1355         Intent innerIntent = new Intent("android.os.cts.TEST_BROADCAST_ACTION");
1356         intent.putExtra(IntentLaunchReceiver.INNER_INTENT_KEY, innerIntent);
1357 
1358         assertViolation(UNSAFE_INTENT_LAUNCH, () -> context.sendBroadcast(intent));
1359     }
1360 
1361     @Test
testUnsafeIntentLaunch_ParceledIntentDataCopy_ThrowsViolation()1362     public void testUnsafeIntentLaunch_ParceledIntentDataCopy_ThrowsViolation() throws Exception {
1363         // This test verifies a violation is reported when data is copied from a parceled Intent
1364         // without sanitation or validation to a new Intent that is being created to launch a new
1365         // component.
1366         StrictMode.setVmPolicy(
1367                 new StrictMode.VmPolicy.Builder()
1368                         .detectUnsafeIntentLaunch()
1369                         .penaltyLog()
1370                         .build());
1371         Context context = getContext();
1372         Intent intent = IntentLaunchActivity.getUnsafeDataCopyFromIntentTestIntent(context);
1373 
1374         assertViolation(UNSAFE_INTENT_LAUNCH, () -> context.startActivity(intent));
1375     }
1376 
1377     @Test
testUnsafeIntentLaunch_UnsafeDataCopy_ThrowsViolation()1378     public void testUnsafeIntentLaunch_UnsafeDataCopy_ThrowsViolation() throws Exception {
1379         // This test verifies a violation is reported when data is copied from unparceled extras
1380         // without sanitation or validation to a new Intent that is being created to launch a new
1381         // component.
1382         StrictMode.setVmPolicy(
1383                 new StrictMode.VmPolicy.Builder()
1384                         .detectUnsafeIntentLaunch()
1385                         .penaltyLog()
1386                         .build());
1387         Context context = getContext();
1388         Intent intent = IntentLaunchActivity.getUnsafeDataCopyFromExtrasTestIntent(context);
1389 
1390         assertViolation(UNSAFE_INTENT_LAUNCH, () -> context.startActivity(intent));
1391     }
1392 
1393     @Test
testUnsafeIntentLaunch_DataCopyFromIntentDeliveredToProtectedComponent_NoViolation()1394     public void testUnsafeIntentLaunch_DataCopyFromIntentDeliveredToProtectedComponent_NoViolation()
1395         throws Exception {
1396         // This test verifies a violation is not reported when data is copied from the Intent
1397         // delivered to a protected component.
1398         StrictMode.setVmPolicy(
1399                 new StrictMode.VmPolicy.Builder()
1400                         .detectUnsafeIntentLaunch()
1401                         .penaltyLog()
1402                         .build());
1403         Context context = getContext();
1404         Intent intent =
1405                 IntentLaunchActivity.getDataCopyFromDeliveredIntentWithUnparceledExtrasTestIntent(
1406                         context);
1407 
1408         assertNoViolation(() -> context.startActivity(intent));
1409     }
1410 
1411     @Test
testUnsafeIntentLaunch_UnsafeIntentFromUriLaunch_ThrowsViolation()1412     public void testUnsafeIntentLaunch_UnsafeIntentFromUriLaunch_ThrowsViolation()
1413             throws Exception {
1414         // Intents can also be delivered as URI strings and parsed with Intent#parseUri. This test
1415         // verifies if an Intent is parsed from a URI string and launched without any additional
1416         // sanitation / validation then a violation is reported.
1417         StrictMode.setVmPolicy(
1418                 new StrictMode.VmPolicy.Builder()
1419                         .detectUnsafeIntentLaunch()
1420                         .penaltyLog()
1421                         .build());
1422         Context context = getContext();
1423         Intent intent =
1424                 IntentLaunchActivity.getUnsafeIntentFromUriLaunchTestIntent(context);
1425 
1426         assertViolation(UNSAFE_INTENT_LAUNCH, () -> context.startActivity(intent));
1427     }
1428 
1429     @Test
testUnsafeIntentLaunch_SafeIntentFromUriLaunch_NoViolation()1430     public void testUnsafeIntentLaunch_SafeIntentFromUriLaunch_NoViolation() throws Exception {
1431         // The documentation for Intent#URI_ALLOW_UNSAFE recommend using the CATEGORY_BROWSABLE
1432         // when launching an Intent parsed from a URI; while an explicit Intent will still be
1433         // delivered to the target component with this category set an implicit Intent will be
1434         // limited to components with Intent-filters that handle this category. This test verifies
1435         // an implicit Intent parsed from a URI with the browsable category set does not result in
1436         // an UnsafeIntentLaunch StrictMode violation.
1437         StrictMode.setVmPolicy(
1438                 new StrictMode.VmPolicy.Builder()
1439                         .detectUnsafeIntentLaunch()
1440                         .penaltyLog()
1441                         .build());
1442         Context context = getContext();
1443         Intent intent =
1444                 IntentLaunchActivity.getSafeIntentFromUriLaunchTestIntent(context);
1445 
1446         assertNoViolation(() -> context.startActivity(intent));
1447     }
1448 
createWindowContext()1449     private Context createWindowContext() {
1450         final Display display = getContext().getSystemService(DisplayManager.class)
1451                 .getDisplay(DEFAULT_DISPLAY);
1452         return getContext().createWindowContext(display, TYPE_APPLICATION_OVERLAY,
1453                 null /* options */);
1454     }
1455 
runWithRemoteServiceBound(Context context, Consumer<ISecondary> consumer)1456     private static void runWithRemoteServiceBound(Context context, Consumer<ISecondary> consumer)
1457             throws ExecutionException, InterruptedException, RemoteException {
1458         BlockingQueue<IBinder> binderHolder = new ArrayBlockingQueue<>(1);
1459         ServiceConnection secondaryConnection =
1460                 new ServiceConnection() {
1461                     public void onServiceConnected(ComponentName className, IBinder service) {
1462                         binderHolder.add(service);
1463                     }
1464 
1465                     public void onServiceDisconnected(ComponentName className) {
1466                         binderHolder.drainTo(new ArrayList<>());
1467                     }
1468                 };
1469         Intent intent = new Intent(REMOTE_SERVICE_ACTION);
1470         intent.setPackage(context.getPackageName());
1471 
1472         Intent secondaryIntent = new Intent(ISecondary.class.getName());
1473         secondaryIntent.setPackage(context.getPackageName());
1474         assertThat(
1475                         context.bindService(
1476                                 secondaryIntent, secondaryConnection, Context.BIND_AUTO_CREATE))
1477                 .isTrue();
1478         IBinder binder = binderHolder.take();
1479         assertThat(binder.queryLocalInterface(binder.getInterfaceDescriptor())).isNull();
1480         consumer.accept(ISecondary.Stub.asInterface(binder));
1481         context.unbindService(secondaryConnection);
1482         context.stopService(intent);
1483     }
1484 
assertViolation(String expected, ThrowingRunnable r)1485     private static void assertViolation(String expected, ThrowingRunnable r) throws Exception {
1486         inspectViolation(r, info -> assertThat(info.getStackTrace()).contains(expected),
1487                 VIOLATION_TIMEOUT_IN_SECOND);
1488     }
1489 
assertNoViolation(ThrowingRunnable r)1490     private static void assertNoViolation(ThrowingRunnable r) throws Exception {
1491         inspectViolation(r, info -> assertWithMessage("Unexpected violation").that(info).isNull(),
1492                 NO_VIOLATION_TIMEOUT_IN_SECOND);
1493     }
1494 
inspectViolation(ThrowingRunnable violating, Consumer<ViolationInfo> consume)1495     private static void inspectViolation(ThrowingRunnable violating,
1496             Consumer<ViolationInfo> consume) throws Exception {
1497         inspectViolation(violating, consume, VIOLATION_TIMEOUT_IN_SECOND);
1498     }
1499 
inspectViolation(ThrowingRunnable violating, Consumer<ViolationInfo> consume, int timeout)1500     private static void inspectViolation(ThrowingRunnable violating,
1501             Consumer<ViolationInfo> consume, int timeout) throws Exception {
1502         final LinkedBlockingQueue<ViolationInfo> violations = new LinkedBlockingQueue<>();
1503         StrictMode.setViolationLogger(violations::add);
1504 
1505         try {
1506             violating.run();
1507             consume.accept(violations.poll(timeout, TimeUnit.SECONDS));
1508         } finally {
1509             StrictMode.setViolationLogger(null);
1510         }
1511     }
1512 
hasInternetConnection()1513     private boolean hasInternetConnection() {
1514         final PackageManager pm = getContext().getPackageManager();
1515         return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
1516                 || pm.hasSystemFeature(PackageManager.FEATURE_WIFI)
1517                 || pm.hasSystemFeature(PackageManager.FEATURE_ETHERNET);
1518     }
1519 
1520     public static class TestService extends Service {
1521         private final TestToken mToken = new TestToken();
1522 
1523         @Override
onBind(Intent intent)1524         public IBinder onBind(Intent intent) {
1525             return mToken;
1526         }
1527 
1528         public class TestToken extends Binder {
getService()1529             TestService getService() {
1530                 return TestService.this;
1531             }
1532         }
1533     }
1534 
1535     public static class TestWindowService extends WindowProviderService {
1536         private final TestToken mToken = new TestToken();
1537 
1538         @Override
onBind(Intent intent)1539         public IBinder onBind(Intent intent) {
1540             return mToken;
1541         }
1542 
1543         @Override
getWindowType()1544         public int getWindowType() {
1545             return TYPE_APPLICATION_OVERLAY;
1546         }
1547 
1548         public class TestToken extends Binder {
getService()1549             TestWindowService getService() {
1550                 return TestWindowService.this;
1551             }
1552         }
1553     }
1554 }
1555