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