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