加载 outApk 资源

30 阅读1分钟
package com.lsposed.lspatch;  
  
import android.app.ActivityThread;  
import android.app.Application;  
import android.app.LoadedApk;  
import android.content.pm.ApplicationInfo;  
import android.content.res.CompatibilityInfo;  
import android.os.Bundle;  
import android.view.View;  
import android.widget.TextView;  
  
import androidx.appcompat.app.AppCompatActivity;  
  
import com.lsposed.lspatch.databinding.ActivityMainBinding;  
  
import java.io.InputStream;  
import java.lang.reflect.Method;  
import java.nio.file.Files;  
import java.nio.file.Path;  
import java.nio.file.Paths;  
import java.util.Map;  
import java.util.zip.ZipFile;  
  
import de.robv.android.xposed.XposedHelpers;  
  
public class MainActivity extends AppCompatActivity {  
  
// Used to load the 'lspatch' library on application startup.  
static {  
System.loadLibrary("lspatch001");  
}  
  
private ActivityMainBinding binding;  
  
@Override  
protected void onCreate(Bundle savedInstanceState) {  
super.onCreate(savedInstanceState);  
  
binding = ActivityMainBinding.inflate(getLayoutInflater());  
setContentView(binding.getRoot());  
  
// Example of a call to a native method  
TextView tv = binding.sampleText;  
tv.setText(stringFromJNI());  
}  
  
/**  
* A native method that is implemented by the 'lspatch' native library,  
* which is packaged with this application.  
*/  
public native String stringFromJNI();  
  
public void onTest1(View view) {  
// 加载外部APk  
loadApk0(this);  
}  
  
@Override  
protected void onStop() {  
super.onStop();  
}  
  
public void loadOutApk() {  
try {  
ActivityThread activityThread = ActivityThread.currentActivityThread();  
var mBoundApplication = XposedHelpers.getObjectField(activityThread, "mBoundApplication");  
  
LoadedApk stubLoadedApk = (LoadedApk) XposedHelpers.getObjectField(mBoundApplication, "info");  
var appInfo = (ApplicationInfo) XposedHelpers.getObjectField(mBoundApplication, "appInfo");  
// var compatInfo = (CompatibilityInfo) XposedHelpers.getObjectField(mBoundApplication, "compatInfo");  
var compatInfo = (CompatibilityInfo) XposedHelpers.newInstance(CompatibilityInfo.class);  
var baseClassLoader = stubLoadedApk.getClassLoader();  
  
// 目标apk路径  
Path originPath = Paths.get(appInfo.dataDir, "cache/origin/");  
Path cacheApkPath;  
try (ZipFile sourceFile = new ZipFile(appInfo.sourceDir)) {  
cacheApkPath = originPath.resolve(sourceFile.getEntry("assets/aspplication-debug.apk").getCrc() + ".apk");  
}  
  
// 修改apk路径  
String sourceDir = appInfo.sourceDir;  
String publicSourceDir = appInfo.publicSourceDir;  
appInfo.sourceDir = cacheApkPath.toString();  
appInfo.publicSourceDir = cacheApkPath.toString();  
  
// 修改lib路径  
String nativeLibraryDir = appInfo.nativeLibraryDir;  
appInfo.nativeLibraryDir = cacheApkPath.toString() + "!/lib/arm64-v8a";  
  
// 修改Application  
String className = appInfo.className;  
String name = appInfo.name;  
appInfo.className = "com.aspirin.aspplication.BaseApplication";  
appInfo.name = "com.aspirin.aspplication.BaseApplication";  
// appInfo.packageName = "com.aspirin.aspplication";  
// appInfo.appComponentFactory = config.appComponentFactory;  
  
if (!Files.exists(cacheApkPath)) {  
Files.createDirectories(originPath);  
try (InputStream is = baseClassLoader.getResourceAsStream("assets/aspplication-debug.apk")) {  
Files.copy(is, cacheApkPath);  
}  
}  
cacheApkPath.toFile().setWritable(false);  
  
Map<?, ?> mPackages = (Map<?, ?>) XposedHelpers.getObjectField(activityThread, "mPackages");  
mPackages.remove(appInfo.packageName);  
LoadedApk outAppLoadedApk = activityThread.getPackageInfoNoCheck(appInfo, compatInfo);  
XposedHelpers.setObjectField(outAppLoadedApk, "mBaseClassLoader", baseClassLoader);  
  
LspModuleClassLoader lspModuleClassLoader = new LspModuleClassLoader(cacheApkPath.toString(), appInfo.sourceDir, appInfo.nativeLibraryDir, baseClassLoader);  
XposedHelpers.setObjectField(outAppLoadedApk, "mClassLoader", lspModuleClassLoader);  
ClassLoader outClassLoader = outAppLoadedApk.getClassLoader();  
  
XposedHelpers.setObjectField(mBoundApplication, "info", outAppLoadedApk);  
// 创建Application  
Application outApplication = (Application) XposedHelpers.callMethod(outAppLoadedApk, "makeApplication", false, XposedHelpers.getObjectField(activityThread, "mInstrumentation"));  
  
// 恢复  
appInfo.sourceDir = sourceDir;  
appInfo.publicSourceDir = publicSourceDir;  
appInfo.nativeLibraryDir = nativeLibraryDir;  
appInfo.className = className;  
appInfo.name = name;  
  
// 跳转Apk  
Class<?> ToastUtilsClass = outClassLoader.loadClass("com.aspirin.aspplication.ToastUtils");  
Method showToast = ToastUtilsClass.getDeclaredMethod("showActivity");  
showToast.invoke(null);  
} catch (Throwable e) {  
System.out.println("loadOutApk throwable: " + e);  
e.printStackTrace();  
}  
}  
  
/**  
* 加载外部APk  
*  
* @param mainActivity  
*/  
private void loadApk0(MainActivity mainActivity) {  
loadOutApk();  
// Intent intent = new Intent(mainActivity, MainActivity.class);  
// intent.putExtra("out_app", "com.lsposed.lspatch.MainActivity");  
// startActivity(intent);  
}  
}