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