package me.ele.amigo;
import android.content.Context;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.os.Build;
import android.util.ArrayMap;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.HashMap;
import static me.ele.amigo.compat.ActivityThreadCompat.instance;
import static me.ele.amigo.reflect.FieldUtils.getField;
import static me.ele.amigo.reflect.FieldUtils.readField;
import static me.ele.amigo.reflect.FieldUtils.writeField;
import static me.ele.amigo.reflect.MethodUtils.invokeMethod;
import static me.ele.amigo.reflect.MethodUtils.invokeStaticMethod;
class PatchResourceLoader {
private static AssetManager originalAssetManager = null;
static void loadPatchResources(Context context, String checksum) throws Exception {
AssetManager newAssetManager = AssetManager.class.newInstance();
invokeMethod(newAssetManager, "addAssetPath", PatchApks.getInstance(context).patchPath(checksum));
invokeMethod(newAssetManager, "ensureStringBlocks");
replaceAssetManager(context, newAssetManager);
}
private static void replaceAssetManager(Context context, AssetManager newAssetManager)
throws Exception {
Collection<WeakReference<Resources>> references;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Class<?> resourcesManagerClass = Class.forName("android.app.ResourcesManager");
Object resourcesManager = invokeStaticMethod(resourcesManagerClass, "getInstance");
if (getField(resourcesManagerClass, "mActiveResources") != null) {
ArrayMap<?, WeakReference<Resources>> arrayMap =
(ArrayMap) readField(resourcesManager, "mActiveResources", true);
references = arrayMap.values();
} else {
references = (Collection) readField(resourcesManager, "mResourceReferences", true);
}
} else {
HashMap<?, WeakReference<Resources>> map =
(HashMap) readField(instance(), "mActiveResources", true);
references = map.values();
}
AssetManager assetManager = context != null ? context.getAssets() : null;
for (WeakReference<Resources> wr : references) {
Resources resources = wr.get();
if (resources == null) continue;
try {
writeField(resources, "mAssets", newAssetManager);
originalAssetManager = assetManager;
} catch (Throwable ignore) {
Object resourceImpl = readField(resources, "mResourcesImpl", true);
writeField(resourceImpl, "mAssets", newAssetManager);
}
resources.updateConfiguration(resources.getConfiguration(),
resources.getDisplayMetrics());
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
for (WeakReference<Resources> wr : references) {
Resources resources = wr.get();
if (resources == null) continue;
// android.util.Pools$SynchronizedPool<TypedArray>
Object typedArrayPool = readField(resources, "mTypedArrayPool", true);
// Clear all the pools
while (invokeMethod(typedArrayPool, "acquire") != null) ;
}
}
}
static void revertLoadPatchResources() throws Exception {
final AssetManager assetManager = originalAssetManager;
originalAssetManager = null;
if (assetManager != null) {
replaceAssetManager(null, assetManager);
}
}
}