xref: /aosp_15_r20/external/robolectric/shadows/framework/src/main/java/org/robolectric/shadows/ShadowLoadedApk.java (revision e6ba16074e6af37d123cb567d575f496bf0a58ee)
1 package org.robolectric.shadows;
2 
3 import static org.robolectric.shadow.api.Shadow.newInstanceOf;
4 import static org.robolectric.util.reflector.Reflector.reflector;
5 
6 import android.app.Application;
7 import android.app.LoadedApk;
8 import android.content.pm.ApplicationInfo;
9 import android.content.pm.PackageManager.NameNotFoundException;
10 import android.content.res.Resources;
11 import android.os.Build.VERSION_CODES;
12 import org.robolectric.RuntimeEnvironment;
13 import org.robolectric.annotation.Implementation;
14 import org.robolectric.annotation.Implements;
15 import org.robolectric.annotation.RealObject;
16 import org.robolectric.util.reflector.Accessor;
17 import org.robolectric.util.reflector.ForType;
18 
19 @Implements(value = LoadedApk.class, isInAndroidSdk = false)
20 public class ShadowLoadedApk {
21   @RealObject private LoadedApk realLoadedApk;
22   private boolean isClassLoaderInitialized = false;
23   private final Object classLoaderLock = new Object();
24 
25   @Implementation
getClassLoader()26   public ClassLoader getClassLoader() {
27     // The AppComponentFactory was introduced from SDK 28.
28     if (RuntimeEnvironment.getApiLevel() >= VERSION_CODES.P) {
29       synchronized (classLoaderLock) {
30         if (!isClassLoaderInitialized) {
31           isClassLoaderInitialized = true;
32           tryInitAppComponentFactory(realLoadedApk);
33         }
34       }
35     }
36     return this.getClass().getClassLoader();
37   }
38 
39   @Implementation(minSdk = VERSION_CODES.O)
getSplitClassLoader(String splitName)40   public ClassLoader getSplitClassLoader(String splitName) throws NameNotFoundException {
41     return this.getClass().getClassLoader();
42   }
43 
tryInitAppComponentFactory(LoadedApk realLoadedApk)44   private void tryInitAppComponentFactory(LoadedApk realLoadedApk) {
45     if (RuntimeEnvironment.getApiLevel() >= VERSION_CODES.P) {
46       ApplicationInfo applicationInfo = realLoadedApk.getApplicationInfo();
47       if (applicationInfo == null || applicationInfo.appComponentFactory == null) {
48         return;
49       }
50       _LoadedApk_ loadedApkReflector = reflector(_LoadedApk_.class, realLoadedApk);
51       if (!loadedApkReflector.getIncludeCode()) {
52         return;
53       }
54       String fullQualifiedClassName =
55           calculateFullQualifiedClassName(
56               applicationInfo.appComponentFactory, applicationInfo.packageName);
57       android.app.AppComponentFactory factory =
58           (android.app.AppComponentFactory) newInstanceOf(fullQualifiedClassName);
59       if (factory == null) {
60         factory = new android.app.AppComponentFactory();
61       }
62       loadedApkReflector.setAppFactory(factory);
63     }
64   }
65 
calculateFullQualifiedClassName(String className, String packageName)66   private String calculateFullQualifiedClassName(String className, String packageName) {
67     if (packageName == null) {
68       return className;
69     }
70     return className.startsWith(".") ? packageName + className : className;
71   }
72 
73   /** Accessor interface for {@link LoadedApk}'s internals. */
74   @ForType(LoadedApk.class)
75   public interface _LoadedApk_ {
76 
77     @Accessor("mApplication")
setApplication(Application application)78     void setApplication(Application application);
79 
80     @Accessor("mResources")
setResources(Resources resources)81     void setResources(Resources resources);
82 
83     @Accessor("mIncludeCode")
getIncludeCode()84     boolean getIncludeCode();
85 
86     @Accessor("mAppComponentFactory")
setAppFactory(Object appFactory)87     void setAppFactory(Object appFactory);
88   }
89 }
90