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