1 package org.robolectric.android.internal; 2 3 import static android.os.Build.VERSION_CODES.P; 4 import static android.os.Build.VERSION_CODES.Q; 5 import static org.robolectric.shadow.api.Shadow.newInstanceOf; 6 import static org.robolectric.util.reflector.Reflector.reflector; 7 8 import android.annotation.SuppressLint; 9 import android.app.ActivityThread; 10 import android.app.AppCompatCallbacks; 11 import android.app.Application; 12 import android.app.Instrumentation; 13 import android.app.LoadedApk; 14 import android.content.BroadcastReceiver; 15 import android.content.ComponentName; 16 import android.content.Context; 17 import android.content.IntentFilter; 18 import android.content.pm.ApplicationInfo; 19 import android.content.pm.PackageParser; 20 import android.content.pm.PackageParser.Package; 21 import android.content.res.Resources; 22 import android.graphics.Bitmap; 23 import android.os.Build; 24 import android.os.Build.VERSION_CODES; 25 import android.os.Bundle; 26 import android.os.Handler; 27 import android.os.Looper; 28 import android.provider.FontsContract; 29 import android.util.DisplayMetrics; 30 import androidx.test.platform.app.InstrumentationRegistry; 31 import com.google.common.annotations.VisibleForTesting; 32 import com.google.common.base.Preconditions; 33 import com.google.common.base.Supplier; 34 import com.google.common.base.Suppliers; 35 import java.nio.file.Path; 36 import java.security.Security; 37 import java.security.cert.Certificate; 38 import java.security.cert.X509Certificate; 39 import java.util.ArrayList; 40 import java.util.Arrays; 41 import java.util.List; 42 import java.util.Locale; 43 import javax.inject.Named; 44 import javax.net.ssl.HostnameVerifier; 45 import javax.net.ssl.HttpsURLConnection; 46 import javax.net.ssl.SSLException; 47 import javax.net.ssl.SSLSession; 48 import org.bouncycastle.jce.provider.BouncyCastleProvider; 49 import org.conscrypt.OkHostnameVerifier; 50 import org.conscrypt.OpenSSLProvider; 51 import org.robolectric.RuntimeEnvironment; 52 import org.robolectric.android.Bootstrap; 53 import org.robolectric.annotation.Config; 54 import org.robolectric.annotation.ConscryptMode; 55 import org.robolectric.annotation.GraphicsMode; 56 import org.robolectric.annotation.LooperMode; 57 import org.robolectric.annotation.SQLiteMode; 58 import org.robolectric.annotation.experimental.LazyApplication.LazyLoad; 59 import org.robolectric.config.ConfigurationRegistry; 60 import org.robolectric.internal.ShadowProvider; 61 import org.robolectric.internal.TestEnvironment; 62 import org.robolectric.manifest.AndroidManifest; 63 import org.robolectric.manifest.BroadcastReceiverData; 64 import org.robolectric.nativeruntime.DefaultNativeRuntimeLoader; 65 import org.robolectric.pluginapi.Sdk; 66 import org.robolectric.pluginapi.TestEnvironmentLifecyclePlugin; 67 import org.robolectric.pluginapi.config.ConfigurationStrategy.Configuration; 68 import org.robolectric.shadow.api.Shadow; 69 import org.robolectric.shadows.ClassNameResolver; 70 import org.robolectric.shadows.ShadowActivityThread; 71 import org.robolectric.shadows.ShadowActivityThread._ActivityThread_; 72 import org.robolectric.shadows.ShadowActivityThread._AppBindData_; 73 import org.robolectric.shadows.ShadowApplication; 74 import org.robolectric.shadows.ShadowContextImpl._ContextImpl_; 75 import org.robolectric.shadows.ShadowInstrumentation; 76 import org.robolectric.shadows.ShadowInstrumentation._Instrumentation_; 77 import org.robolectric.shadows.ShadowLegacyLooper; 78 import org.robolectric.shadows.ShadowLoadedApk._LoadedApk_; 79 import org.robolectric.shadows.ShadowLog; 80 import org.robolectric.shadows.ShadowLooper; 81 import org.robolectric.shadows.ShadowPackageManager; 82 import org.robolectric.shadows.ShadowPackageParser; 83 import org.robolectric.shadows.ShadowPausedLooper; 84 import org.robolectric.shadows.ShadowView; 85 import org.robolectric.util.Logger; 86 import org.robolectric.util.PerfStatsCollector; 87 import org.robolectric.util.ReflectionHelpers; 88 import org.robolectric.util.ReflectionHelpers.ClassParameter; 89 import org.robolectric.util.Scheduler; 90 import org.robolectric.util.TempDirectory; 91 import org.robolectric.util.Util; 92 import org.robolectric.versioning.AndroidVersions; 93 import org.robolectric.versioning.AndroidVersions.V; 94 95 @SuppressLint("NewApi") 96 public class AndroidTestEnvironment implements TestEnvironment { 97 98 private static final String CONSCRYPT_PROVIDER = "Conscrypt"; 99 100 private final Sdk compileSdk; 101 102 private final int apiLevel; 103 104 private boolean loggingInitialized = false; 105 private final Path sdkJarPath; 106 private final ShadowProvider[] shadowProviders; 107 private final TestEnvironmentLifecyclePlugin[] testEnvironmentLifecyclePlugins; 108 private final Locale initialLocale = Locale.getDefault(); 109 AndroidTestEnvironment( @amed"runtimeSdk") Sdk runtimeSdk, @Named("compileSdk") Sdk compileSdk, ShadowProvider[] shadowProviders, TestEnvironmentLifecyclePlugin[] lifecyclePlugins)110 public AndroidTestEnvironment( 111 @Named("runtimeSdk") Sdk runtimeSdk, 112 @Named("compileSdk") Sdk compileSdk, 113 ShadowProvider[] shadowProviders, 114 TestEnvironmentLifecyclePlugin[] lifecyclePlugins) { 115 this.compileSdk = compileSdk; 116 117 apiLevel = runtimeSdk.getApiLevel(); 118 sdkJarPath = runtimeSdk.getJarPath(); 119 this.shadowProviders = shadowProviders; 120 this.testEnvironmentLifecyclePlugins = lifecyclePlugins; 121 122 ReflectionHelpers.setStaticField(RuntimeEnvironment.class, "apiLevel", apiLevel); 123 } 124 125 @Override setUpApplicationState( String tmpDirName, Configuration configuration, AndroidManifest appManifest)126 public void setUpApplicationState( 127 String tmpDirName, Configuration configuration, AndroidManifest appManifest) { 128 Preconditions.checkArgument(tmpDirName != null && !tmpDirName.isEmpty()); 129 Config config = configuration.get(Config.class); 130 131 ConfigurationRegistry.instance = new ConfigurationRegistry(configuration.map()); 132 133 for (TestEnvironmentLifecyclePlugin e : testEnvironmentLifecyclePlugins) { 134 e.onSetupApplicationState(); 135 } 136 137 clearEnvironment(); 138 139 // Starting in Android V and above, the native runtime does not support begin lazy-loaded, it 140 // must be loaded upfront. 141 if (shouldLoadNativeRuntime() && RuntimeEnvironment.getApiLevel() >= V.SDK_INT) { 142 DefaultNativeRuntimeLoader.injectAndLoad(); 143 } 144 145 RuntimeEnvironment.setTempDirectory(new TempDirectory(tmpDirName)); 146 if (ShadowLooper.looperMode() == LooperMode.Mode.LEGACY) { 147 RuntimeEnvironment.setMasterScheduler(new Scheduler()); 148 RuntimeEnvironment.setMainThread(Thread.currentThread()); 149 ShadowLegacyLooper.internalInitializeBackgroundThreadScheduler(); 150 } 151 152 if (!loggingInitialized) { 153 ShadowLog.setupLogging(); 154 loggingInitialized = true; 155 } 156 157 ConscryptMode.Mode conscryptMode = configuration.get(ConscryptMode.Mode.class); 158 Security.removeProvider(CONSCRYPT_PROVIDER); 159 if (conscryptMode != ConscryptMode.Mode.OFF) { 160 161 Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); 162 if (Security.getProvider(CONSCRYPT_PROVIDER) == null) { 163 Security.insertProviderAt(new OpenSSLProvider(), 1); 164 } 165 166 HttpsURLConnection.setDefaultHostnameVerifier( 167 new HostnameVerifier() { 168 private final OkHostnameVerifier conscryptVerifier = OkHostnameVerifier.INSTANCE; 169 170 @Override 171 public boolean verify(String hostname, SSLSession session) { 172 try { 173 Certificate[] certificates = session.getPeerCertificates(); 174 X509Certificate[] x509Certificates = 175 Arrays.copyOf(certificates, certificates.length, X509Certificate[].class); 176 return conscryptVerifier.verify(x509Certificates, hostname, session); 177 } catch (SSLException e) { 178 return false; 179 } 180 } 181 }); 182 } 183 184 if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) { 185 Security.addProvider(new BouncyCastleProvider()); 186 } 187 188 android.content.res.Configuration androidConfiguration = 189 new android.content.res.Configuration(); 190 DisplayMetrics displayMetrics = new DisplayMetrics(); 191 192 Bootstrap.applyQualifiers(config.qualifiers(), apiLevel, androidConfiguration, displayMetrics); 193 194 androidConfiguration.fontScale = config.fontScale(); 195 196 if (ShadowView.useRealGraphics()) { 197 Bitmap.setDefaultDensity(displayMetrics.densityDpi); 198 } 199 Locale locale = 200 apiLevel >= VERSION_CODES.N 201 ? androidConfiguration.getLocales().get(0) 202 : androidConfiguration.locale; 203 Locale.setDefault(locale); 204 205 if (ShadowLooper.looperMode() == LooperMode.Mode.LEGACY) { 206 if (Looper.myLooper() == null) { 207 Looper.prepareMainLooper(); 208 } 209 ShadowLooper.getShadowMainLooper().resetScheduler(); 210 } else { 211 ShadowPausedLooper.resetLoopers(); 212 RuntimeEnvironment.setMasterScheduler(new LooperDelegatingScheduler(Looper.getMainLooper())); 213 } 214 215 preloadClasses(apiLevel); 216 217 RuntimeEnvironment.setAndroidFrameworkJarPath(sdkJarPath); 218 Bootstrap.setDisplayConfiguration(androidConfiguration, displayMetrics); 219 220 Instrumentation instrumentation = createInstrumentation(); 221 InstrumentationRegistry.registerInstance(instrumentation, new Bundle()); 222 Supplier<Application> applicationSupplier = createApplicationSupplier(appManifest, config); 223 RuntimeEnvironment.setApplicationSupplier(applicationSupplier); 224 225 if (configuration.get(LazyLoad.class) == LazyLoad.ON) { 226 RuntimeEnvironment.setConfiguredApplicationClass( 227 getApplicationClass(appManifest, config, new ApplicationInfo())); 228 } else { 229 // force eager load of the application 230 RuntimeEnvironment.getApplication(); 231 } 232 } 233 234 // If certain Android classes are required to be loaded in a particular order, do so here. 235 // Android's Zygote has a class preloading mechanism, and there have been obscure crashes caused 236 // by Android bugs requiring a specific initialization order. preloadClasses(int apiLevel)237 private void preloadClasses(int apiLevel) { 238 if (apiLevel >= Q) { 239 // Preload URI to avoid a static initializer cycle that can be caused by using Uri.Builder 240 // before Uri.EMPTY. 241 try { 242 Class.forName("android.net.Uri", true, this.getClass().getClassLoader()); 243 } catch (ClassNotFoundException e) { 244 throw new RuntimeException(e); 245 } 246 } 247 } 248 249 // TODO Move synchronization logic into its own class for better readability createApplicationSupplier( AndroidManifest appManifest, Config config)250 private Supplier<Application> createApplicationSupplier( 251 AndroidManifest appManifest, Config config) { 252 final ActivityThread activityThread = (ActivityThread) RuntimeEnvironment.getActivityThread(); 253 final _ActivityThread_ _activityThread_ = reflector(_ActivityThread_.class, activityThread); 254 final ShadowActivityThread shadowActivityThread = Shadow.extract(activityThread); 255 256 return Suppliers.memoize( 257 () -> 258 PerfStatsCollector.getInstance() 259 .measure( 260 "installAndCreateApplication", 261 () -> 262 installAndCreateApplication( 263 appManifest, 264 config, 265 shadowActivityThread, 266 _activityThread_, 267 activityThread.getInstrumentation()))); 268 } 269 installAndCreateApplication( AndroidManifest appManifest, Config config, ShadowActivityThread shadowActivityThread, _ActivityThread_ activityThreadReflector, Instrumentation androidInstrumentation)270 private Application installAndCreateApplication( 271 AndroidManifest appManifest, 272 Config config, 273 ShadowActivityThread shadowActivityThread, 274 _ActivityThread_ activityThreadReflector, 275 Instrumentation androidInstrumentation) { 276 ActivityThread activityThread = (ActivityThread) RuntimeEnvironment.getActivityThread(); 277 278 Context systemContextImpl = reflector(_ContextImpl_.class).createSystemContext(activityThread); 279 RuntimeEnvironment.systemContext = systemContextImpl; 280 281 Application dummyInitialApplication = new Application(); 282 activityThreadReflector.setInitialApplication(dummyInitialApplication); 283 ShadowApplication shadowInitialApplication = Shadow.extract(dummyInitialApplication); 284 shadowInitialApplication.callAttach(systemContextImpl); 285 286 Package parsedPackage = loadAppPackage(appManifest); 287 288 ApplicationInfo applicationInfo = parsedPackage.applicationInfo; 289 Class<? extends Application> applicationClass = 290 getApplicationClass(appManifest, config, applicationInfo); 291 applicationInfo.className = applicationClass.getName(); 292 293 ComponentName actualComponentName = 294 new ComponentName( 295 applicationInfo.packageName, androidInstrumentation.getClass().getSimpleName()); 296 ReflectionHelpers.setField(androidInstrumentation, "mComponent", actualComponentName); 297 298 // unclear why, but prior to P the processName wasn't set 299 if (apiLevel < P && applicationInfo.processName == null) { 300 applicationInfo.processName = parsedPackage.packageName; 301 } 302 303 setUpPackageStorage(applicationInfo, parsedPackage); 304 305 // Bit of a hack... Context.createPackageContext() is called before the application is created. 306 // It calls through 307 // to ActivityThread for the package which in turn calls the PackageManagerService directly. 308 // This works for now 309 // but it might be nicer to have ShadowPackageManager implementation move into the service as 310 // there is also lots of 311 // code in there that can be reusable, e.g: the XxxxIntentResolver code. 312 ShadowActivityThread.setApplicationInfo(applicationInfo); 313 314 // Bootstrap.getConfiguration gets any potential updates to configuration via 315 // RuntimeEnvironment.setQualifiers. 316 android.content.res.Configuration androidConfiguration = Bootstrap.getConfiguration(); 317 shadowActivityThread.setCompatConfiguration(androidConfiguration); 318 319 Bootstrap.setUpDisplay(); 320 activityThread.applyConfigurationToResources(androidConfiguration); 321 322 Application application = ReflectionHelpers.callConstructor(applicationClass); 323 RuntimeEnvironment.setConfiguredApplicationClass(applicationClass); 324 325 RuntimeEnvironment.application = application; 326 327 if (application != null) { 328 final Class<?> appBindDataClass; 329 try { 330 appBindDataClass = Class.forName("android.app.ActivityThread$AppBindData"); 331 } catch (ClassNotFoundException e) { 332 throw new RuntimeException(e); 333 } 334 final Object appBindData = ReflectionHelpers.callConstructor(appBindDataClass); 335 final _AppBindData_ _appBindData_ = reflector(_AppBindData_.class, appBindData); 336 _appBindData_.setProcessName(parsedPackage.packageName); 337 _appBindData_.setAppInfo(applicationInfo); 338 activityThreadReflector.setBoundApplication(appBindData); 339 340 final LoadedApk loadedApk = 341 activityThread.getPackageInfo(applicationInfo, null, Context.CONTEXT_INCLUDE_CODE); 342 final _LoadedApk_ _loadedApk_ = reflector(_LoadedApk_.class, loadedApk); 343 344 Context contextImpl = 345 reflector(_ContextImpl_.class).createAppContext(activityThread, loadedApk); 346 ShadowPackageManager shadowPackageManager = Shadow.extract(contextImpl.getPackageManager()); 347 shadowPackageManager.addPackageInternal(parsedPackage); 348 activityThreadReflector.setInitialApplication(application); 349 ShadowApplication shadowApplication = Shadow.extract(application); 350 shadowApplication.callAttach(contextImpl); 351 reflector(_ContextImpl_.class, contextImpl).setOuterContext(application); 352 if (apiLevel >= VERSION_CODES.O) { 353 reflector(_ContextImpl_.class, contextImpl) 354 .setClassLoader(this.getClass().getClassLoader()); 355 } 356 357 Resources appResources = application.getResources(); 358 _loadedApk_.setResources(appResources); 359 _loadedApk_.setApplication(application); 360 if (RuntimeEnvironment.getApiLevel() >= VERSION_CODES.O) { 361 // Preload fonts resources 362 FontsContract.setApplicationContextForResources(application); 363 } 364 registerBroadcastReceivers(application, appManifest, loadedApk); 365 366 appResources.updateConfiguration(androidConfiguration, Bootstrap.getDisplayMetrics()); 367 368 // Circumvent the 'No Compatibility callbacks set!' log. See #8509 369 if (apiLevel >= AndroidVersions.V.SDK_INT) { 370 // Adds loggableChanges parameter. 371 ReflectionHelpers.callStaticMethod( 372 AppCompatCallbacks.class, 373 "install", 374 ClassParameter.from(long[].class, new long[0]), 375 ClassParameter.from(long[].class, new long[0])); 376 } else if (apiLevel >= AndroidVersions.R.SDK_INT) { 377 // Invoke the previous version. 378 ReflectionHelpers.callStaticMethod( 379 AppCompatCallbacks.class, "install", ClassParameter.from(long[].class, new long[0])); 380 } 381 382 PerfStatsCollector.getInstance() 383 .measure( 384 "application onCreate()", 385 () -> androidInstrumentation.callApplicationOnCreate(application)); 386 } 387 388 return application; 389 } 390 loadAppPackage(AndroidManifest appManifest)391 private Package loadAppPackage(AndroidManifest appManifest) { 392 return PerfStatsCollector.getInstance() 393 .measure("parse package", () -> loadAppPackage_measured(appManifest)); 394 } 395 loadAppPackage_measured(AndroidManifest appManifest)396 private Package loadAppPackage_measured(AndroidManifest appManifest) { 397 398 Package parsedPackage; 399 400 // lazy load the compile sdk jar path. It should only be needed when the deprecated 401 // AttributeSetBuilder is used 402 RuntimeEnvironment.setCompileTimeSystemResources(compileSdk::getJarPath); 403 404 Path packageFile = appManifest.getApkFile(); 405 if (packageFile != null) { 406 parsedPackage = ShadowPackageParser.callParsePackage(packageFile); 407 } else { 408 parsedPackage = new Package("org.robolectric.default"); 409 parsedPackage.applicationInfo.targetSdkVersion = appManifest.getTargetSdkVersion(); 410 } 411 412 if (parsedPackage != null 413 && parsedPackage.applicationInfo != null 414 && RuntimeEnvironment.getApiLevel() >= P) { 415 parsedPackage.applicationInfo.appComponentFactory = appManifest.getAppComponentFactory(); 416 } 417 return parsedPackage; 418 } 419 420 @VisibleForTesting getApplicationClass( AndroidManifest appManifest, Config config, ApplicationInfo applicationInfo)421 static Class<? extends Application> getApplicationClass( 422 AndroidManifest appManifest, Config config, ApplicationInfo applicationInfo) { 423 Class<? extends Application> applicationClass = null; 424 if (config != null && !Config.Builder.isDefaultApplication(config.application())) { 425 if (config.application().getCanonicalName() != null) { 426 try { 427 applicationClass = ClassNameResolver.resolve(null, config.application().getName()); 428 } catch (ClassNotFoundException e) { 429 throw new RuntimeException(e); 430 } 431 } 432 } else if (appManifest != null && appManifest.getApplicationName() != null) { 433 try { 434 applicationClass = 435 ClassNameResolver.resolve( 436 appManifest.getPackageName(), 437 getTestApplicationName(appManifest.getApplicationName())); 438 } catch (ClassNotFoundException e) { 439 // no problem 440 } 441 442 if (applicationClass == null) { 443 try { 444 applicationClass = 445 ClassNameResolver.resolve( 446 appManifest.getPackageName(), appManifest.getApplicationName()); 447 } catch (ClassNotFoundException e) { 448 throw new RuntimeException(e); 449 } 450 } 451 } else { 452 if (applicationInfo.className != null) { 453 try { 454 applicationClass = 455 (Class<? extends Application>) 456 Class.forName(getTestApplicationName(applicationInfo.className)); 457 } catch (ClassNotFoundException e) { 458 // no problem 459 } 460 461 if (applicationClass == null) { 462 try { 463 applicationClass = 464 (Class<? extends Application>) Class.forName(applicationInfo.className); 465 } catch (ClassNotFoundException e) { 466 throw new RuntimeException(e); 467 } 468 } 469 } else { 470 applicationClass = Application.class; 471 } 472 } 473 474 return applicationClass; 475 } 476 477 @VisibleForTesting getTestApplicationName(String applicationName)478 static String getTestApplicationName(String applicationName) { 479 int lastDot = applicationName.lastIndexOf('.'); 480 if (lastDot > -1) { 481 return applicationName.substring(0, lastDot) 482 + ".Test" 483 + applicationName.substring(lastDot + 1); 484 } else { 485 return "Test" + applicationName; 486 } 487 } 488 pickInstrumentation()489 protected Instrumentation pickInstrumentation() { 490 return new RoboMonitoringInstrumentation(); 491 } 492 createInstrumentation()493 private Instrumentation createInstrumentation() { 494 Instrumentation androidInstrumentation = pickInstrumentation(); 495 androidInstrumentation.runOnMainSync( 496 () -> { 497 ActivityThread activityThread = ReflectionHelpers.callConstructor(ActivityThread.class); 498 ReflectionHelpers.setStaticField( 499 ActivityThread.class, "sMainThreadHandler", new Handler(Looper.getMainLooper())); 500 reflector(_ActivityThread_.class, activityThread) 501 .setInstrumentation(androidInstrumentation); 502 RuntimeEnvironment.setActivityThread(activityThread); 503 504 Application dummyInitialApplication = new Application(); 505 final ComponentName dummyInitialComponent = 506 new ComponentName("", androidInstrumentation.getClass().getSimpleName()); 507 reflector(_Instrumentation_.class, androidInstrumentation) 508 .init( 509 activityThread, 510 dummyInitialApplication, 511 dummyInitialApplication, 512 dummyInitialComponent, 513 null, 514 null); 515 }); 516 517 androidInstrumentation.onCreate(new Bundle()); 518 return androidInstrumentation; 519 } 520 521 @Override tearDownApplication()522 public void tearDownApplication() { 523 if (RuntimeEnvironment.application != null) { 524 ShadowInstrumentation.runOnMainSyncNoIdle(RuntimeEnvironment.getApplication()::onTerminate); 525 ShadowInstrumentation.getInstrumentation().finish(1, new Bundle()); 526 } 527 } 528 529 /** 530 * Clear the global variables set and used by AndroidTestEnvironment TODO Move synchronization 531 * logic into its own class for better readability 532 */ clearEnvironment()533 private void clearEnvironment() { 534 // Need to clear both the application supplier and the instrumentation here *before* clearing 535 // RuntimeEnvironment.application. That way if RuntimeEnvironment.getApplication() or 536 // ApplicationProvider.getApplicationContext() get called in between here and the end of this 537 // method, we don't accidentally trigger application loading with stale references 538 RuntimeEnvironment.setApplicationSupplier(null); 539 InstrumentationRegistry.registerInstance(null, new Bundle()); 540 RuntimeEnvironment.setActivityThread(null); 541 RuntimeEnvironment.application = null; 542 RuntimeEnvironment.systemContext = null; 543 Bootstrap.resetDisplayConfiguration(); 544 } 545 546 @Override checkStateAfterTestFailure(Throwable t)547 public void checkStateAfterTestFailure(Throwable t) throws Throwable { 548 if (hasUnexecutedRunnables()) { 549 t.addSuppressed(new UnExecutedRunnablesException()); 550 } 551 throw t; 552 } 553 554 private static final class UnExecutedRunnablesException extends Exception { 555 UnExecutedRunnablesException()556 UnExecutedRunnablesException() { 557 super( 558 "Main looper has queued unexecuted runnables. " 559 + "This might be the cause of the test failure. " 560 + "You might need a shadowOf(Looper.getMainLooper()).idle() call."); 561 } 562 563 @Override fillInStackTrace()564 public synchronized Throwable fillInStackTrace() { 565 setStackTrace(new StackTraceElement[0]); 566 return this; // no stack trace, wouldn't be useful anyway 567 } 568 } 569 hasUnexecutedRunnables()570 private boolean hasUnexecutedRunnables() { 571 ShadowLooper shadowLooper = Shadow.extract(Looper.getMainLooper()); 572 return !shadowLooper.isIdle(); 573 } 574 575 @Override resetState()576 public void resetState() { 577 Locale.setDefault(initialLocale); 578 List<Throwable> exceptions = new ArrayList<>(); 579 for (ShadowProvider provider : shadowProviders) { 580 try { 581 provider.reset(); 582 } catch (Throwable e) { 583 exceptions.add(e); 584 } 585 } 586 587 if (!exceptions.isEmpty()) { 588 Throwable first = exceptions.remove(0); 589 for (Throwable t : exceptions) { 590 first.addSuppressed(t); 591 } 592 Util.sneakyThrow(first); 593 } 594 } 595 596 // TODO(christianw): reconcile with ShadowPackageManager.setUpPackageStorage setUpPackageStorage( ApplicationInfo applicationInfo, PackageParser.Package parsedPackage)597 private void setUpPackageStorage( 598 ApplicationInfo applicationInfo, PackageParser.Package parsedPackage) { 599 // TempDirectory tempDirectory = RuntimeEnvironment.getTempDirectory(); 600 // packageInfo.setVolumeUuid(tempDirectory.createIfNotExists(packageInfo.packageName + 601 // "-dataDir").toAbsolutePath().toString()); 602 603 applicationInfo.publicSourceDir = parsedPackage.codePath; 604 applicationInfo.sourceDir = parsedPackage.codePath; 605 606 applicationInfo.dataDir = createTempDir(applicationInfo.packageName + "-dataDir"); 607 608 if (RuntimeEnvironment.getApiLevel() >= Build.VERSION_CODES.N) { 609 applicationInfo.credentialProtectedDataDir = createTempDir("userDataDir"); 610 applicationInfo.deviceProtectedDataDir = createTempDir("deviceDataDir"); 611 } 612 } 613 createTempDir(String name)614 private String createTempDir(String name) { 615 return RuntimeEnvironment.getTempDirectory() 616 .createIfNotExists(name) 617 .toAbsolutePath() 618 .toString(); 619 } 620 newBroadcastReceiverFromP( String receiverClassName, LoadedApk loadedApk)621 private static BroadcastReceiver newBroadcastReceiverFromP( 622 String receiverClassName, LoadedApk loadedApk) { 623 ClassLoader classLoader = Shadow.class.getClassLoader(); 624 if (loadedApk == null || loadedApk.getAppFactory() == null) { 625 return (BroadcastReceiver) newInstanceOf(receiverClassName); 626 } else { 627 try { 628 return loadedApk.getAppFactory().instantiateReceiver(classLoader, receiverClassName, null); 629 } catch (ReflectiveOperationException e) { 630 Logger.warn( 631 "Failed to initialize receiver %s with AppComponentFactory %s: %s", 632 receiverClassName, loadedApk.getAppFactory(), e); 633 } 634 } 635 return null; 636 } 637 shouldLoadNativeRuntime()638 private static boolean shouldLoadNativeRuntime() { 639 GraphicsMode.Mode graphicsMode = ConfigurationRegistry.get(GraphicsMode.Mode.class); 640 SQLiteMode.Mode sqliteMode = ConfigurationRegistry.get(SQLiteMode.Mode.class); 641 return graphicsMode == GraphicsMode.Mode.NATIVE || sqliteMode == SQLiteMode.Mode.NATIVE; 642 } 643 644 // TODO move/replace this with packageManager 645 @VisibleForTesting registerBroadcastReceivers( Application application, AndroidManifest androidManifest, LoadedApk loadedApk)646 static void registerBroadcastReceivers( 647 Application application, AndroidManifest androidManifest, LoadedApk loadedApk) { 648 for (BroadcastReceiverData receiver : androidManifest.getBroadcastReceivers()) { 649 IntentFilter filter = new IntentFilter(); 650 for (String action : receiver.getActions()) { 651 filter.addAction(action); 652 } 653 String receiverClassName = receiver.getName(); 654 if (loadedApk != null && RuntimeEnvironment.getApiLevel() >= P) { 655 application.registerReceiver( 656 newBroadcastReceiverFromP(receiverClassName, loadedApk), filter); 657 } else { 658 application.registerReceiver((BroadcastReceiver) newInstanceOf(receiverClassName), filter); 659 } 660 } 661 } 662 } 663