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