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 com.google.common.truth.Truth.assertThat; 20 import static com.google.common.truth.Truth.assertWithMessage; 21 22 import static org.junit.Assert.assertTrue; 23 import static org.junit.Assert.fail; 24 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.ServiceConnection; 29 import android.content.pm.PackageManager; 30 import android.net.TrafficStats; 31 import android.net.Uri; 32 import android.os.IBinder; 33 import android.os.RemoteException; 34 import android.os.StrictMode; 35 import android.os.StrictMode.ThreadPolicy.Builder; 36 import android.os.StrictMode.ViolationInfo; 37 import android.os.strictmode.CleartextNetworkViolation; 38 import android.os.strictmode.CustomViolation; 39 import android.os.strictmode.DiskReadViolation; 40 import android.os.strictmode.DiskWriteViolation; 41 import android.os.strictmode.ExplicitGcViolation; 42 import android.os.strictmode.FileUriExposedViolation; 43 import android.os.strictmode.InstanceCountViolation; 44 import android.os.strictmode.LeakedClosableViolation; 45 import android.os.strictmode.NetworkViolation; 46 import android.os.strictmode.NonSdkApiUsedViolation; 47 import android.os.strictmode.UntaggedSocketViolation; 48 import android.os.strictmode.Violation; 49 import android.platform.test.annotations.AppModeFull; 50 import android.platform.test.annotations.AppModeInstant; 51 import android.system.Os; 52 import android.system.OsConstants; 53 import android.util.Log; 54 55 import androidx.test.InstrumentationRegistry; 56 import androidx.test.runner.AndroidJUnit4; 57 58 import org.junit.After; 59 import org.junit.Before; 60 import org.junit.Test; 61 import org.junit.runner.RunWith; 62 63 import java.io.File; 64 import java.io.FileDescriptor; 65 import java.io.FileInputStream; 66 import java.io.FileNotFoundException; 67 import java.io.FileOutputStream; 68 import java.io.IOException; 69 import java.net.HttpURLConnection; 70 import java.net.Socket; 71 import java.net.URL; 72 import java.util.ArrayList; 73 import java.util.List; 74 import java.util.concurrent.ArrayBlockingQueue; 75 import java.util.concurrent.BlockingQueue; 76 import java.util.concurrent.CountDownLatch; 77 import java.util.concurrent.ExecutionException; 78 import java.util.concurrent.LinkedBlockingQueue; 79 import java.util.concurrent.TimeUnit; 80 import java.util.function.Consumer; 81 82 /** Tests for {@link StrictMode} */ 83 @RunWith(AndroidJUnit4.class) 84 public class StrictModeTest { 85 private static final String TAG = "StrictModeTest"; 86 private static final String REMOTE_SERVICE_ACTION = "android.app.REMOTESERVICE"; 87 88 private StrictMode.ThreadPolicy mThreadPolicy; 89 private StrictMode.VmPolicy mVmPolicy; 90 getContext()91 private Context getContext() { 92 return InstrumentationRegistry.getContext(); 93 } 94 95 @Before setUp()96 public void setUp() { 97 mThreadPolicy = StrictMode.getThreadPolicy(); 98 mVmPolicy = StrictMode.getVmPolicy(); 99 } 100 101 @After tearDown()102 public void tearDown() { 103 StrictMode.setThreadPolicy(mThreadPolicy); 104 StrictMode.setVmPolicy(mVmPolicy); 105 } 106 107 public interface ThrowingRunnable { run()108 void run() throws Exception; 109 } 110 111 @Test testThreadBuilder()112 public void testThreadBuilder() throws Exception { 113 StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().detectDiskReads().penaltyLog().build(); 114 StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder(policy).build()); 115 116 final File test = File.createTempFile("foo", "bar"); 117 inspectViolation( 118 test::exists, 119 info -> { 120 assertThat(info.getViolationDetails()).isNull(); 121 assertThat(info.getStackTrace()).contains("DiskReadViolation"); 122 }); 123 } 124 125 @Test testUnclosedCloseable()126 public void testUnclosedCloseable() throws Exception { 127 StrictMode.setVmPolicy( 128 new StrictMode.VmPolicy.Builder().detectLeakedClosableObjects().build()); 129 130 inspectViolation( 131 () -> leakCloseable("leaked.txt"), 132 info -> { 133 assertThat(info.getViolationDetails()) 134 .isEqualTo( 135 "A resource was acquired at attached stack trace but never released. See java.io.Closeable for information on avoiding resource leaks."); 136 assertThat(info.getStackTrace()) 137 .contains("Explicit termination method 'close' not called"); 138 assertThat(info.getStackTrace()).contains("leakCloseable"); 139 assertThat(info.getViolationClass()) 140 .isAssignableTo(LeakedClosableViolation.class); 141 }); 142 } 143 leakCloseable(String fileName)144 private void leakCloseable(String fileName) throws InterruptedException { 145 final CountDownLatch finalizedSignal = new CountDownLatch(1); 146 try { 147 new FileOutputStream(new File(getContext().getFilesDir(), fileName)) { 148 @Override 149 protected void finalize() throws IOException { 150 super.finalize(); 151 finalizedSignal.countDown(); 152 } 153 }; 154 } catch (FileNotFoundException e) { 155 throw new RuntimeException(e); 156 } 157 Runtime.getRuntime().gc(); 158 Runtime.getRuntime().runFinalization(); 159 // Sometimes it needs extra prodding. 160 if (!finalizedSignal.await(5, TimeUnit.SECONDS)) { 161 Runtime.getRuntime().gc(); 162 Runtime.getRuntime().runFinalization(); 163 } 164 } 165 166 @Test testClassInstanceLimit()167 public void testClassInstanceLimit() throws Exception { 168 StrictMode.setVmPolicy( 169 new StrictMode.VmPolicy.Builder() 170 .setClassInstanceLimit(LimitedClass.class, 1) 171 .build()); 172 List<LimitedClass> references = new ArrayList<>(); 173 assertNoViolation(() -> references.add(new LimitedClass())); 174 references.add(new LimitedClass()); 175 inspectViolation( 176 StrictMode::conditionallyCheckInstanceCounts, 177 info -> assertThat(info.getViolationClass()) 178 .isAssignableTo(InstanceCountViolation.class)); 179 } 180 181 private static final class LimitedClass {} 182 183 /** Insecure connection should be detected */ 184 @AppModeFull 185 @Test testCleartextNetwork()186 public void testCleartextNetwork() throws Exception { 187 if (!hasInternetConnection()) { 188 Log.i(TAG, "testCleartextNetwork() ignored on device without Internet"); 189 return; 190 } 191 192 StrictMode.setVmPolicy( 193 new StrictMode.VmPolicy.Builder().detectCleartextNetwork().penaltyLog().build()); 194 195 inspectViolation( 196 () -> 197 ((HttpURLConnection) new URL("http://example.com/").openConnection()) 198 .getResponseCode(), 199 info -> assertThat(info.getViolationClass()) 200 .isAssignableTo(CleartextNetworkViolation.class)); 201 } 202 203 /** Secure connection should be ignored */ 204 @Test testEncryptedNetwork()205 public void testEncryptedNetwork() throws Exception { 206 if (!hasInternetConnection()) { 207 Log.i(TAG, "testEncryptedNetwork() ignored on device without Internet"); 208 return; 209 } 210 211 StrictMode.setVmPolicy( 212 new StrictMode.VmPolicy.Builder().detectCleartextNetwork().penaltyLog().build()); 213 214 assertNoViolation( 215 () -> 216 ((HttpURLConnection) new URL("https://example.com/").openConnection()) 217 .getResponseCode()); 218 } 219 220 @Test testFileUriExposure()221 public void testFileUriExposure() throws Exception { 222 StrictMode.setVmPolicy( 223 new StrictMode.VmPolicy.Builder().detectFileUriExposure().penaltyLog().build()); 224 225 final Uri badUri = Uri.fromFile(new File("/sdcard/meow.jpg")); 226 inspectViolation( 227 () -> { 228 Intent intent = new Intent(Intent.ACTION_VIEW); 229 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 230 intent.setDataAndType(badUri, "image/jpeg"); 231 getContext().startActivity(intent); 232 }, 233 info -> { 234 assertThat(info.getStackTrace()).contains(badUri + " exposed beyond app"); 235 }); 236 237 final Uri goodUri = Uri.parse("content://com.example/foobar"); 238 assertNoViolation( 239 () -> { 240 Intent intent = new Intent(Intent.ACTION_VIEW); 241 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 242 intent.setDataAndType(goodUri, "image/jpeg"); 243 getContext().startActivity(intent); 244 }); 245 } 246 247 @Test testFileUriExposure_Chooser()248 public void testFileUriExposure_Chooser() throws Exception { 249 StrictMode.setVmPolicy( 250 new StrictMode.VmPolicy.Builder().detectFileUriExposure().penaltyLog().build()); 251 252 final Uri badUri = Uri.fromFile(new File("/sdcard/meow.jpg")); 253 inspectViolation( 254 () -> { 255 Intent intent = new Intent(Intent.ACTION_SEND); 256 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 257 intent.setType("image/jpeg"); 258 intent.putExtra(Intent.EXTRA_STREAM, badUri); 259 260 Intent chooser = Intent.createChooser(intent, "CTS"); 261 chooser.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 262 getContext().startActivity(chooser); 263 }, 264 info -> { 265 assertThat(info.getStackTrace()).contains(badUri + " exposed beyond app"); 266 }); 267 } 268 269 @Test testContentUriWithoutPermission()270 public void testContentUriWithoutPermission() throws Exception { 271 StrictMode.setVmPolicy( 272 new StrictMode.VmPolicy.Builder() 273 .detectContentUriWithoutPermission() 274 .penaltyLog() 275 .build()); 276 277 final Uri uri = Uri.parse("content://com.example/foobar"); 278 inspectViolation( 279 () -> { 280 Intent intent = new Intent(Intent.ACTION_VIEW); 281 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 282 intent.setDataAndType(uri, "image/jpeg"); 283 getContext().startActivity(intent); 284 }, 285 info -> 286 assertThat(info.getStackTrace()) 287 .contains(uri + " exposed beyond app")); 288 289 assertNoViolation( 290 () -> { 291 Intent intent = new Intent(Intent.ACTION_VIEW); 292 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 293 intent.setDataAndType(uri, "image/jpeg"); 294 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 295 getContext().startActivity(intent); 296 }); 297 } 298 299 @AppModeFull 300 @Test testUntaggedSocketsHttp()301 public void testUntaggedSocketsHttp() throws Exception { 302 if (!hasInternetConnection()) { 303 Log.i(TAG, "testUntaggedSockets() ignored on device without Internet"); 304 return; 305 } 306 307 StrictMode.setVmPolicy( 308 new StrictMode.VmPolicy.Builder().detectUntaggedSockets().penaltyLog().build()); 309 310 inspectViolation( 311 () -> 312 ((HttpURLConnection) new URL("http://example.com/").openConnection()) 313 .getResponseCode(), 314 info -> assertThat(info.getViolationClass()) 315 .isAssignableTo(UntaggedSocketViolation.class)); 316 317 assertNoViolation( 318 () -> { 319 TrafficStats.setThreadStatsTag(0xDECAFBAD); 320 try { 321 ((HttpURLConnection) new URL("http://example.com/").openConnection()) 322 .getResponseCode(); 323 } finally { 324 TrafficStats.clearThreadStatsTag(); 325 } 326 }); 327 } 328 329 @Test testUntaggedSocketsRaw()330 public void testUntaggedSocketsRaw() throws Exception { 331 if (!hasInternetConnection()) { 332 Log.i(TAG, "testUntaggedSockets() ignored on device without Internet"); 333 return; 334 } 335 336 StrictMode.setVmPolicy( 337 new StrictMode.VmPolicy.Builder().detectUntaggedSockets().penaltyLog().build()); 338 339 assertNoViolation( 340 () -> { 341 TrafficStats.setThreadStatsTag(0xDECAFBAD); 342 try (Socket socket = new Socket("example.com", 80)) { 343 socket.getOutputStream().close(); 344 } finally { 345 TrafficStats.clearThreadStatsTag(); 346 } 347 }); 348 349 inspectViolation( 350 () -> { 351 try (Socket socket = new Socket("example.com", 80)) { 352 socket.getOutputStream().close(); 353 } 354 }, 355 info -> assertThat(info.getViolationClass()) 356 .isAssignableTo(UntaggedSocketViolation.class)); 357 } 358 359 private static final int PERMISSION_USER_ONLY = 0600; 360 361 @Test testRead()362 public void testRead() throws Exception { 363 final File test = File.createTempFile("foo", "bar"); 364 final File dir = test.getParentFile(); 365 366 final FileInputStream is = new FileInputStream(test); 367 final FileDescriptor fd = 368 Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, PERMISSION_USER_ONLY); 369 370 StrictMode.setThreadPolicy( 371 new StrictMode.ThreadPolicy.Builder().detectDiskReads().penaltyLog().build()); 372 inspectViolation( 373 test::exists, 374 info -> { 375 assertThat(info.getViolationDetails()).isNull(); 376 assertThat(info.getStackTrace()).contains("DiskReadViolation"); 377 }); 378 379 Consumer<ViolationInfo> assertDiskReadPolicy = info -> assertThat( 380 info.getViolationClass()).isAssignableTo(DiskReadViolation.class); 381 inspectViolation(test::exists, assertDiskReadPolicy); 382 inspectViolation(test::length, assertDiskReadPolicy); 383 inspectViolation(dir::list, assertDiskReadPolicy); 384 inspectViolation(is::read, assertDiskReadPolicy); 385 386 inspectViolation(() -> new FileInputStream(test), assertDiskReadPolicy); 387 inspectViolation( 388 () -> Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, PERMISSION_USER_ONLY), 389 assertDiskReadPolicy); 390 inspectViolation(() -> Os.read(fd, new byte[10], 0, 1), assertDiskReadPolicy); 391 } 392 393 @Test testWrite()394 public void testWrite() throws Exception { 395 File file = File.createTempFile("foo", "bar"); 396 397 final FileOutputStream os = new FileOutputStream(file); 398 final FileDescriptor fd = 399 Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, PERMISSION_USER_ONLY); 400 401 StrictMode.setThreadPolicy( 402 new StrictMode.ThreadPolicy.Builder().detectDiskWrites().penaltyLog().build()); 403 404 inspectViolation( 405 file::createNewFile, 406 info -> { 407 assertThat(info.getViolationDetails()).isNull(); 408 assertThat(info.getStackTrace()).contains("DiskWriteViolation"); 409 }); 410 411 Consumer<ViolationInfo> assertDiskWritePolicy = info -> assertThat( 412 info.getViolationClass()).isAssignableTo(DiskWriteViolation.class); 413 414 inspectViolation(() -> File.createTempFile("foo", "bar"), assertDiskWritePolicy); 415 inspectViolation(() -> new FileOutputStream(file), assertDiskWritePolicy); 416 inspectViolation(file::delete, assertDiskWritePolicy); 417 inspectViolation(file::createNewFile, assertDiskWritePolicy); 418 inspectViolation(() -> os.write(32), assertDiskWritePolicy); 419 420 inspectViolation( 421 () -> Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, PERMISSION_USER_ONLY), 422 assertDiskWritePolicy); 423 inspectViolation(() -> Os.write(fd, new byte[10], 0, 1), assertDiskWritePolicy); 424 inspectViolation(() -> Os.fsync(fd), assertDiskWritePolicy); 425 inspectViolation( 426 () -> file.renameTo(new File(file.getParent(), "foobar")), assertDiskWritePolicy); 427 } 428 429 @AppModeFull 430 @Test testNetwork()431 public void testNetwork() throws Exception { 432 if (!hasInternetConnection()) { 433 Log.i(TAG, "testUntaggedSockets() ignored on device without Internet"); 434 return; 435 } 436 437 StrictMode.setThreadPolicy( 438 new StrictMode.ThreadPolicy.Builder().detectNetwork().penaltyLog().build()); 439 440 inspectViolation( 441 () -> { 442 try (Socket socket = new Socket("example.com", 80)) { 443 socket.getOutputStream().close(); 444 } 445 }, 446 info -> assertThat(info.getViolationClass()) 447 .isAssignableTo(NetworkViolation.class)); 448 inspectViolation( 449 () -> 450 ((HttpURLConnection) new URL("http://example.com/").openConnection()) 451 .getResponseCode(), 452 info -> assertThat(info.getViolationClass()) 453 .isAssignableTo(NetworkViolation.class)); 454 } 455 456 @Test testExplicitGc()457 public void testExplicitGc() throws Exception { 458 StrictMode.setThreadPolicy( 459 new StrictMode.ThreadPolicy.Builder().detectExplicitGc().penaltyLog().build()); 460 461 inspectViolation( 462 () -> { Runtime.getRuntime().gc(); }, 463 info -> assertThat(info.getViolationClass()) 464 .isAssignableTo(ExplicitGcViolation.class)); 465 } 466 467 @Test testViolationAcrossBinder()468 public void testViolationAcrossBinder() throws Exception { 469 runWithRemoteServiceBound( 470 getContext(), 471 service -> { 472 StrictMode.setThreadPolicy( 473 new Builder().detectDiskWrites().penaltyLog().build()); 474 475 try { 476 inspectViolation( 477 () -> service.performDiskWrite(), 478 (info) -> { 479 assertThat(info.getViolationClass()) 480 .isAssignableTo(DiskWriteViolation.class); 481 assertThat(info.getViolationDetails()) 482 .isNull(); // Disk write has no message. 483 assertThat(info.getStackTrace()) 484 .contains("DiskWriteViolation"); 485 assertThat(info.getStackTrace()) 486 .contains( 487 "at android.os.StrictMode$AndroidBlockGuardPolicy.onWriteToDisk"); 488 assertThat(info.getStackTrace()) 489 .contains("# via Binder call with stack:"); 490 assertThat(info.getStackTrace()) 491 .contains( 492 "at android.os.cts.ISecondary$Stub$Proxy.performDiskWrite"); 493 }); 494 assertNoViolation(() -> service.getPid()); 495 } catch (Exception e) { 496 throw new RuntimeException(e); 497 } 498 }); 499 } 500 checkNonSdkApiUsageViolation(boolean blacklist, String className, String methodName, Class<?>... paramTypes)501 private void checkNonSdkApiUsageViolation(boolean blacklist, String className, 502 String methodName, Class<?>... paramTypes) throws Exception { 503 Class<?> clazz = Class.forName(className); 504 inspectViolation( 505 () -> { 506 try { 507 java.lang.reflect.Method m = clazz.getDeclaredMethod(methodName, paramTypes); 508 if (blacklist) { 509 fail(); 510 } 511 } catch (NoSuchMethodException expected) { 512 if (!blacklist) { 513 fail(); 514 } 515 } 516 }, 517 info -> { 518 assertThat(info).isNotNull(); 519 assertThat(info.getViolationClass()) 520 .isAssignableTo(NonSdkApiUsedViolation.class); 521 assertThat(info.getViolationDetails()).contains(methodName); 522 assertThat(info.getStackTrace()).contains("checkNonSdkApiUsageViolation"); 523 } 524 ); 525 } 526 527 @Test testNonSdkApiUsage()528 public void testNonSdkApiUsage() throws Exception { 529 StrictMode.VmPolicy oldVmPolicy = StrictMode.getVmPolicy(); 530 StrictMode.ThreadPolicy oldThreadPolicy = StrictMode.getThreadPolicy(); 531 try { 532 StrictMode.setVmPolicy( 533 new StrictMode.VmPolicy.Builder().detectNonSdkApiUsage().build()); 534 checkNonSdkApiUsageViolation( 535 true, "dalvik.system.VMRuntime", "setHiddenApiExemptions", String[].class); 536 // verify that mutliple uses of a light greylist API are detected. 537 checkNonSdkApiUsageViolation(false, "dalvik.system.VMRuntime", "getRuntime"); 538 checkNonSdkApiUsageViolation(false, "dalvik.system.VMRuntime", "getRuntime"); 539 540 // Verify that the VM policy is turned off after a call to permitNonSdkApiUsage. 541 StrictMode.setVmPolicy( 542 new StrictMode.VmPolicy.Builder().permitNonSdkApiUsage().build()); 543 assertNoViolation(() -> { 544 Class<?> clazz = Class.forName("dalvik.system.VMRuntime"); 545 try { 546 clazz.getDeclaredMethod("getRuntime"); 547 } catch (NoSuchMethodException maybe) { 548 } 549 }); 550 } finally { 551 StrictMode.setVmPolicy(oldVmPolicy); 552 StrictMode.setThreadPolicy(oldThreadPolicy); 553 } 554 } 555 556 @Test testThreadPenaltyListener()557 public void testThreadPenaltyListener() throws Exception { 558 final BlockingQueue<Violation> violations = new ArrayBlockingQueue<>(1); 559 StrictMode.setThreadPolicy( 560 new StrictMode.ThreadPolicy.Builder().detectCustomSlowCalls() 561 .penaltyListener(getContext().getMainExecutor(), (v) -> { 562 violations.add(v); 563 }).build()); 564 565 StrictMode.noteSlowCall("foo"); 566 567 final Violation v = violations.poll(5, TimeUnit.SECONDS); 568 assertTrue(v instanceof CustomViolation); 569 } 570 571 @Test testVmPenaltyListener()572 public void testVmPenaltyListener() throws Exception { 573 final BlockingQueue<Violation> violations = new ArrayBlockingQueue<>(1); 574 StrictMode.setVmPolicy( 575 new StrictMode.VmPolicy.Builder().detectFileUriExposure() 576 .penaltyListener(getContext().getMainExecutor(), (v) -> { 577 violations.add(v); 578 }).build()); 579 580 Intent intent = new Intent(Intent.ACTION_VIEW); 581 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 582 intent.setDataAndType(Uri.fromFile(new File("/sdcard/meow.jpg")), "image/jpeg"); 583 getContext().startActivity(intent); 584 585 final Violation v = violations.poll(5, TimeUnit.SECONDS); 586 assertTrue(v instanceof FileUriExposedViolation); 587 } 588 589 @AppModeInstant 590 @Test testNoCleartextHttpTrafficAllowed()591 public void testNoCleartextHttpTrafficAllowed() throws Exception { 592 if (!hasInternetConnection()) { 593 Log.i(TAG, "testNoCleartextHttpTrafficAllowed() ignored on device without Internet"); 594 return; 595 } 596 597 StrictMode.setVmPolicy( 598 new StrictMode.VmPolicy.Builder().detectCleartextNetwork().penaltyLog().build()); 599 600 try { 601 inspectViolation( 602 () -> 603 ((HttpURLConnection) new URL("http://example.com/").openConnection()) 604 .getResponseCode(), 605 info -> assertThat(info.getViolationClass()) 606 .isAssignableTo(CleartextNetworkViolation.class)); 607 fail("Instant app was able to send cleartext http traffic."); 608 } catch (IOException ex) { 609 // Expected 610 } 611 } 612 runWithRemoteServiceBound(Context context, Consumer<ISecondary> consumer)613 private static void runWithRemoteServiceBound(Context context, Consumer<ISecondary> consumer) 614 throws ExecutionException, InterruptedException, RemoteException { 615 BlockingQueue<IBinder> binderHolder = new ArrayBlockingQueue<>(1); 616 ServiceConnection secondaryConnection = 617 new ServiceConnection() { 618 public void onServiceConnected(ComponentName className, IBinder service) { 619 binderHolder.add(service); 620 } 621 622 public void onServiceDisconnected(ComponentName className) { 623 binderHolder.drainTo(new ArrayList<>()); 624 } 625 }; 626 Intent intent = new Intent(REMOTE_SERVICE_ACTION); 627 intent.setPackage(context.getPackageName()); 628 629 Intent secondaryIntent = new Intent(ISecondary.class.getName()); 630 secondaryIntent.setPackage(context.getPackageName()); 631 assertThat( 632 context.bindService( 633 secondaryIntent, secondaryConnection, Context.BIND_AUTO_CREATE)) 634 .isTrue(); 635 IBinder binder = binderHolder.take(); 636 assertThat(binder.queryLocalInterface(binder.getInterfaceDescriptor())).isNull(); 637 consumer.accept(ISecondary.Stub.asInterface(binder)); 638 context.unbindService(secondaryConnection); 639 context.stopService(intent); 640 } 641 assertViolation(String expected, ThrowingRunnable r)642 private static void assertViolation(String expected, ThrowingRunnable r) throws Exception { 643 inspectViolation(r, info -> assertThat(info.getStackTrace()).contains(expected)); 644 } 645 assertNoViolation(ThrowingRunnable r)646 private static void assertNoViolation(ThrowingRunnable r) throws Exception { 647 inspectViolation( 648 r, info -> assertWithMessage("Unexpected violation").that(info).isNull()); 649 } 650 inspectViolation( ThrowingRunnable violating, Consumer<ViolationInfo> consume)651 private static void inspectViolation( 652 ThrowingRunnable violating, Consumer<ViolationInfo> consume) throws Exception { 653 final LinkedBlockingQueue<ViolationInfo> violations = new LinkedBlockingQueue<>(); 654 StrictMode.setViolationLogger(violations::add); 655 656 try { 657 violating.run(); 658 consume.accept(violations.poll(5, TimeUnit.SECONDS)); 659 } finally { 660 StrictMode.setViolationLogger(null); 661 } 662 } 663 hasInternetConnection()664 private boolean hasInternetConnection() { 665 final PackageManager pm = getContext().getPackageManager(); 666 return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) 667 || pm.hasSystemFeature(PackageManager.FEATURE_WIFI) 668 || pm.hasSystemFeature(PackageManager.FEATURE_ETHERNET); 669 } 670 } 671