From e640da4f57acf0f4602e86abf3b6d4e4726e8772 Mon Sep 17 00:00:00 2001 From: NkBe Date: Sun, 8 Mar 2026 21:37:52 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=BC=95=E5=85=A5=E8=BC=95=E9=87=8F?= =?UTF-8?q?=E7=B4=9A=20MicroG=20=E6=94=AF=E6=8F=B4=E8=88=87=20GMS=20?= =?UTF-8?q?=E8=AB=8B=E6=B1=82=E9=87=8D=E5=B0=8E=E5=90=91=E6=A9=9F=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 為 NPatch 引入了原生的 MicroG 整合支援,允許修補後的 Google 應用程式(例如 YouTube)在依賴 Google Play 服務的環境下,透過社群版 MicroG(如 ReVanced GmsCore)正常運作。 詳細改動範圍包含: * ** UI Manager ** - 於 `NewPatchScreen` 的修補選項清單中,新增「啟用 MicroG 支援」核取方塊與相容性說明。 - 更新 `NewPatchViewModel`,透過 `Patcher.kt` 將使用者的 `useMicroG` 選擇狀態以 `--useMicroG` 指令列參數的形式,傳遞給底層修補引擎。 * ** Patcher ** - 於 `NPatch.java` 中新增對 `--useMicroG` 參數的解析邏輯。 - 擴充 `modifyManifestFile` 邏輯:當啟用 MicroG 支援時,自動讀取目標應用的原始簽名並轉碼為 Hex 格式。 - 於 `AndroidManifest.xml` 中動態注入 `fake-signature` 的 `` 節點與 `android.permission.FAKE_PACKAGE_SIGNATURE` 權限,以滿足 MicroG 進行簽名欺騙(Signature Spoofing)時的驗證需求。 * ** Patch Loader ** - 實作 `GmsRedirector.java` 類別,負責在應用程式執行期間動態攔截並重導向 IPC 通訊: 1. 掛鉤 `Intent.setPackage`、`Intent.setComponent` 與建構子,將所有指向 `com.google.android.gms` 與 GSF 的意圖,強制導向至設備上已安裝的社群版 MicroG 套件(優先支援 `app.revanced.android.gms` 與 `org.microg.gms`)。 2. 掛鉤 `ContentResolver`,替換帶有 GMS Authority 的 URI。針對真實 GMS 拋出的 `SecurityException`(憑證拒絕存取)實作了智慧攔截與重試機制。 3. 掛鉤 `PackageManager.getPackageInfo` 實現動態簽名偽裝,使修補後的應用程式將 MicroG 視為官方正版 GMS。 - 於 `LSPApplication.java` 的 `onLoad` 階段,依據 `PatchConfig` 設定檔動態喚醒並初始化 `GmsRedirector`。 * ** Share ** - 更新 `PatchConfig.java` 以儲存與傳遞 `useMicroG` 布林值狀態。 - 於 `Constants.java` 中定義 `REAL_GMS_PACKAGE_NAME` 常數,供各模組統一呼叫。 Co-Authored-By: MrZhongzq <108169409+MrZhongzq@users.noreply.github.com> --- .../main/java/org/lsposed/npatch/Patcher.kt | 1 + .../lsposed/npatch/ui/page/NewPatchScreen.kt | 7 + .../npatch/ui/viewmodel/NewPatchViewModel.kt | 3 +- .../src/main/res/values-zh-rCN/strings.xml | 3 +- .../src/main/res/values-zh-rTW/strings.xml | 2 + manager/src/main/res/values/strings.xml | 2 + .../lsposed/npatch/loader/GmsRedirector.java | 256 ++++++++++++++++++ .../lsposed/npatch/loader/LSPApplication.java | 5 + .../main/java/org/lsposed/patch/NPatch.java | 58 ++-- .../org/lsposed/npatch/share/Constants.java | 1 + .../org/lsposed/npatch/share/PatchConfig.java | 5 +- 11 files changed, 318 insertions(+), 25 deletions(-) create mode 100644 patch-loader/src/main/java/org/lsposed/npatch/loader/GmsRedirector.java diff --git a/manager/src/main/java/org/lsposed/npatch/Patcher.kt b/manager/src/main/java/org/lsposed/npatch/Patcher.kt index 49138f6..d36d2f8 100644 --- a/manager/src/main/java/org/lsposed/npatch/Patcher.kt +++ b/manager/src/main/java/org/lsposed/npatch/Patcher.kt @@ -36,6 +36,7 @@ object Patcher { } if (config.injectProvider) add("--provider") if(injectDex) add("--injectdex") + if (config.useMicroG) add("--useMicroG") if (!MyKeyStore.useDefault) { addAll(arrayOf("-k", MyKeyStore.file.path, Configs.keyStorePassword, Configs.keyStoreAlias, Configs.keyStoreAliasPassword)) } diff --git a/manager/src/main/java/org/lsposed/npatch/ui/page/NewPatchScreen.kt b/manager/src/main/java/org/lsposed/npatch/ui/page/NewPatchScreen.kt index f8914cc..3e08162 100644 --- a/manager/src/main/java/org/lsposed/npatch/ui/page/NewPatchScreen.kt +++ b/manager/src/main/java/org/lsposed/npatch/ui/page/NewPatchScreen.kt @@ -365,6 +365,13 @@ private fun PatchOptionsBody(modifier: Modifier, onAddEmbed: () -> Unit) { title = stringResource(R.string.patch_inject_mt_provider), desc = stringResource(R.string.patch_inject_mt_provider_desc) ) + SettingsCheckBox( + modifier = Modifier.clickable { viewModel.useMicroG = !viewModel.useMicroG }, + checked = viewModel.useMicroG, + icon = Icons.Outlined.CloudSync, + title = stringResource(R.string.patch_use_microg), + desc = stringResource(R.string.patch_use_microg_desc) + ) SettingsCheckBox( modifier = Modifier.clickable { viewModel.outputLog = !viewModel.outputLog }, checked = viewModel.outputLog, diff --git a/manager/src/main/java/org/lsposed/npatch/ui/viewmodel/NewPatchViewModel.kt b/manager/src/main/java/org/lsposed/npatch/ui/viewmodel/NewPatchViewModel.kt index 3fc52cf..53b23d2 100644 --- a/manager/src/main/java/org/lsposed/npatch/ui/viewmodel/NewPatchViewModel.kt +++ b/manager/src/main/java/org/lsposed/npatch/ui/viewmodel/NewPatchViewModel.kt @@ -47,6 +47,7 @@ class NewPatchViewModel : ViewModel() { var injectDex by mutableStateOf(false) var injectProvider by mutableStateOf(false) var outputLog by mutableStateOf(true) + var useMicroG by mutableStateOf(false) var embeddedModules = emptyList() lateinit var patchApp: AppInfo @@ -99,7 +100,7 @@ class NewPatchViewModel : ViewModel() { private fun submitPatch() { Log.d(TAG, "Submit Patch") if (useManager) embeddedModules = emptyList() - val config = PatchConfig(useManager, debuggable, overrideVersionCode, sigBypassLevel, null, null, injectProvider, outputLog, newPackageName) + val config = PatchConfig(useManager, debuggable, overrideVersionCode, sigBypassLevel, null, null, injectProvider, outputLog, newPackageName, useMicroG) patchOptions = Patcher.Options( newPackageName = newPackageName, injectDex = injectDex, diff --git a/manager/src/main/res/values-zh-rCN/strings.xml b/manager/src/main/res/values-zh-rCN/strings.xml index d6d3587..13f7a9f 100644 --- a/manager/src/main/res/values-zh-rCN/strings.xml +++ b/manager/src/main/res/values-zh-rCN/strings.xml @@ -73,7 +73,8 @@ 注入文件提供器以在没有 Root 权限的情况下管理 data 目录的文件 (来自 MT 管理器) 注入加载器 Dex 对那些需要孤立服务进程的应用程序,譬如说浏览器的渲染引擎,请勾选此选项以确保他们正常运行 - 日志输出到 Media 目录 + 强制启用 MicroG 支持 + 重新导向 GMS 请求至社区版 MicroG。适用于 YouTube 等 Google 应用程序,需自行安装对应的 MicroG 服务。 日志输出到 Media 目录 将模块的 Xposed 日志输出到目标应用的 Media 目录 开始修补 返回 diff --git a/manager/src/main/res/values-zh-rTW/strings.xml b/manager/src/main/res/values-zh-rTW/strings.xml index 171b06b..7dfe9f5 100644 --- a/manager/src/main/res/values-zh-rTW/strings.xml +++ b/manager/src/main/res/values-zh-rTW/strings.xml @@ -73,6 +73,8 @@ 注入檔案选取器以在沒有 Root 權限的情況下管理 data 目錄的檔案(來自 MT 管理器) 注入載入器 Dex 對那些需要孤立服務程序的應用程式,譬如說瀏覽器的渲染引擎,請勾選此選項以確保他們正常執行 + 強制啟用 MicroG 支援 + 重新導向 GMS 請求至社群版 MicroG。適用於 YouTube 等 Google 應用程式,需自行安裝對應的 MicroG 服務。 日誌輸出到 Media 目錄 將模組的 Xposed 日誌輸出到目標應用的 Media 目錄 開始打包 diff --git a/manager/src/main/res/values/strings.xml b/manager/src/main/res/values/strings.xml index 5d18112..ce476eb 100644 --- a/manager/src/main/res/values/strings.xml +++ b/manager/src/main/res/values/strings.xml @@ -75,6 +75,8 @@ Inject file providers to manage files in the data directory without root privileges (From MT Manager) Inject loader dex For applications with isolated services, such as the render engines of browsers, please turn on this option to ensure that they work properly. + Force enable MicroG support + Redirect GMS requests to the community version of MicroG (such as ReVanced GmsCore). Applicable to Google apps like YouTube, requires manually installing the corresponding MicroG service. Output Log to Media Directory Output the Xposed log to the target application Media directory. Start Patch diff --git a/patch-loader/src/main/java/org/lsposed/npatch/loader/GmsRedirector.java b/patch-loader/src/main/java/org/lsposed/npatch/loader/GmsRedirector.java new file mode 100644 index 0000000..8c9cec6 --- /dev/null +++ b/patch-loader/src/main/java/org/lsposed/npatch/loader/GmsRedirector.java @@ -0,0 +1,256 @@ +package org.lsposed.npatch.loader; + +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.Signature; +import android.net.Uri; +import android.util.Log; + +import org.lsposed.npatch.share.Constants; + +import de.robv.android.xposed.XC_MethodHook; +import de.robv.android.xposed.XposedBridge; +import de.robv.android.xposed.XposedHelpers; + +public class GmsRedirector { + private static final String TAG = "NPatch-GmsRedirect"; + private static final String REAL_GMS = Constants.REAL_GMS_PACKAGE_NAME; + + // 鎖定社群主流的 MicroG 套件名稱 + private static final String[] MICROG_PACKAGES = { + "app.revanced.android.gms", // ReVanced GmsCore (推薦) + "org.microg.gms", // Original MicroG + }; + + private static String targetGms = null; + private static String originalSignature; + + public static void activate(Context context, String origSig) { + originalSignature = origSig; + + targetGms = findInstalledMicroG(context); + if (targetGms == null) { + Log.w(TAG, "No MicroG/GmsCore found! GMS redirect disabled."); + return; + } + + Log.i(TAG, "Activating GMS redirect: " + REAL_GMS + " -> " + targetGms); + + hookIntentSetPackage(); + hookIntentSetComponent(); + hookIntentResolve(); + hookContentResolverAcquire(); + hookPackageManagerGetPackageInfo(context); + + Log.i(TAG, "GMS redirect hooks installed"); + } + + private static String findInstalledMicroG(Context context) { + PackageManager pm = context.getPackageManager(); + for (String pkg : MICROG_PACKAGES) { + try { + pm.getPackageInfo(pkg, 0); + return pkg; + } catch (PackageManager.NameNotFoundException ignored) {} + } + return null; + } + + private static String redirectPackage(String pkg) { + if (REAL_GMS.equals(pkg) || "com.google.android.gsf".equals(pkg)) { + return targetGms; + } + return null; + } + + private static String redirectAuthority(String authority) { + if (authority == null) return null; + if (authority.startsWith(REAL_GMS + ".")) { + return targetGms + authority.substring(REAL_GMS.length()); + } + if (authority.equals(REAL_GMS)) { + return targetGms; + } + if (authority.startsWith("com.google.android.gsf")) { + return authority.replace("com.google.android.gsf", targetGms); + } + return null; + } + + private static void hookIntentSetPackage() { + try { + XposedBridge.hookAllMethods(Intent.class, "setPackage", new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) { + String pkg = (String) param.args[0]; + String redirected = redirectPackage(pkg); + if (redirected != null) param.args[0] = redirected; + } + }); + } catch (Throwable t) { + Log.e(TAG, "Failed to hook Intent.setPackage", t); + } + } + + private static void hookIntentSetComponent() { + try { + XposedBridge.hookAllMethods(Intent.class, "setComponent", new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) { + ComponentName cn = (ComponentName) param.args[0]; + if (cn != null) { + String redirected = redirectPackage(cn.getPackageName()); + if (redirected != null) { + param.args[0] = new ComponentName(redirected, cn.getClassName()); + } + } + } + }); + } catch (Throwable t) { + Log.e(TAG, "Failed to hook Intent.setComponent", t); + } + } + + private static void hookIntentResolve() { + try { + XposedBridge.hookAllConstructors(Intent.class, new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) { + Intent intent = (Intent) param.thisObject; + ComponentName cn = intent.getComponent(); + if (cn != null) { + String redirected = redirectPackage(cn.getPackageName()); + if (redirected != null) { + intent.setComponent(new ComponentName(redirected, cn.getClassName())); + } + } + String pkg = intent.getPackage(); + if (pkg != null) { + String redirected = redirectPackage(pkg); + if (redirected != null) { + intent.setPackage(redirected); + } + } + } + }); + } catch (Throwable t) { + Log.e(TAG, "Failed to hook Intent constructors", t); + } + } + + private static void hookContentResolverAcquire() { + try { + for (String method : new String[]{ + "acquireProvider", "acquireContentProviderClient", + "acquireUnstableProvider", "acquireUnstableContentProviderClient" + }) { + try { + XposedBridge.hookAllMethods(ContentResolver.class, method, new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) { + if (param.args[0] instanceof Uri) { + Uri uri = (Uri) param.args[0]; + String newAuth = redirectAuthority(uri.getAuthority()); + if (newAuth != null) { + param.args[0] = uri.buildUpon().authority(newAuth).build(); + } + } else if (param.args[0] instanceof String) { + String newAuth = redirectAuthority((String) param.args[0]); + if (newAuth != null) { + param.args[0] = newAuth; + } + } + } + }); + } catch (Throwable ignored) {} + } + + // 攔截 ContentResolver.call,遇到 SecurityException 則自動重試 + try { + XposedBridge.hookAllMethods(ContentResolver.class, "call", new XC_MethodHook() { + @Override + protected void beforeHookedMethod(MethodHookParam param) { + for (int i = 0; i < param.args.length; i++) { + if (param.args[i] instanceof Uri) { + Uri uri = (Uri) param.args[i]; + String newAuth = redirectAuthority(uri.getAuthority()); + if (newAuth != null) { + param.args[i] = uri.buildUpon().authority(newAuth).build(); + } + } else if (param.args[i] instanceof String && i == 0) { + String newAuth = redirectAuthority((String) param.args[i]); + if (newAuth != null) { + param.args[i] = newAuth; + } + } + } + } + + @Override + protected void afterHookedMethod(MethodHookParam param) { + if (param.getThrowable() instanceof SecurityException) { + String msg = param.getThrowable().getMessage(); + if (msg != null && (msg.contains("GoogleCertificatesRslt") || + msg.contains("not allowed") || + msg.contains("Access denied"))) { + Log.i(TAG, "GMS rejected call, retrying with MicroG"); + for (int i = 0; i < param.args.length; i++) { + if (param.args[i] instanceof Uri) { + Uri uri = (Uri) param.args[i]; + String authority = uri.getAuthority(); + if (authority != null && authority.contains(REAL_GMS)) { + param.args[i] = uri.buildUpon() + .authority(authority.replace(REAL_GMS, targetGms)) + .build(); + } + } else if (param.args[i] instanceof String && i == 0) { + String s = (String) param.args[i]; + if (s.contains(REAL_GMS)) { + param.args[i] = s.replace(REAL_GMS, targetGms); + } + } + } + param.setThrowable(null); + param.setResult(null); + } + } + } + }); + } catch (Throwable ignored) {} + } catch (Throwable t) { + Log.e(TAG, "Failed to hook ContentResolver", t); + } + } + + private static void hookPackageManagerGetPackageInfo(Context context) { + try { + XposedHelpers.findAndHookMethod( + context.getPackageManager().getClass(), + "getPackageInfo", + String.class, int.class, + new XC_MethodHook() { + @Override + protected void afterHookedMethod(MethodHookParam param) { + PackageInfo pi = (PackageInfo) param.getResult(); + if (pi != null && targetGms != null) { + if (targetGms.equals(pi.packageName) && (((int) param.args[1]) & PackageManager.GET_SIGNATURES) != 0) { + if (originalSignature != null && !originalSignature.isEmpty()) { + try { + byte[] sigBytes = android.util.Base64.decode(originalSignature, android.util.Base64.DEFAULT); + pi.signatures = new Signature[]{new Signature(sigBytes)}; + } catch (Throwable ignored) {} + } + } + } + } + } + ); + } catch (Throwable t) { + Log.e(TAG, "Failed to hook PackageManager.getPackageInfo", t); + } + } +} \ No newline at end of file diff --git a/patch-loader/src/main/java/org/lsposed/npatch/loader/LSPApplication.java b/patch-loader/src/main/java/org/lsposed/npatch/loader/LSPApplication.java index 3cfbc11..61c2cf8 100644 --- a/patch-loader/src/main/java/org/lsposed/npatch/loader/LSPApplication.java +++ b/patch-loader/src/main/java/org/lsposed/npatch/loader/LSPApplication.java @@ -145,6 +145,11 @@ public class LSPApplication { switchAllClassLoader(); SigBypass.doSigBypass(context, config.sigBypassLevel); + if (config.useMicroG) { + Log.i(TAG, "Activating MicroG redirect via NPatch"); + GmsRedirector.activate(context, config.originalSignature); + } + Log.i(TAG, "NPatch bootstrap completed"); } diff --git a/patch/src/main/java/org/lsposed/patch/NPatch.java b/patch/src/main/java/org/lsposed/patch/NPatch.java index 529fd02..32db440 100644 --- a/patch/src/main/java/org/lsposed/patch/NPatch.java +++ b/patch/src/main/java/org/lsposed/patch/NPatch.java @@ -89,6 +89,12 @@ public class NPatch { @Parameter(names = {"--provider"}, description = "Inject Provider to manager data files") private boolean isInjectProvider = false; + @Parameter(names = {"--installerSource"}, description = "Original app installer source") + private String installerSource = ""; + + @Parameter(names = {"--useMicroG"}, description = "Redirect GMS calls to community MicroG") + private boolean useMicroG = false; + @Parameter(names = {"--outputLog"}, description = "Output Log to Media") private boolean outputLog = true; @@ -287,10 +293,10 @@ public class NPatch { logger.i("Patching apk..."); // modify manifest - final var config = new PatchConfig(useManager, debuggableFlag, overrideVersionCode, sigbypassLevel, originalSignature, appComponentFactory, isInjectProvider, outputLog, newPackage); + final var config = new PatchConfig(useManager, debuggableFlag, overrideVersionCode, sigbypassLevel, originalSignature, appComponentFactory, isInjectProvider, outputLog, newPackage, useMicroG); final var configBytes = new Gson().toJson(config).getBytes(StandardCharsets.UTF_8); final var metadata = Base64.getEncoder().encodeToString(configBytes); - try (var is = new ByteArrayInputStream(modifyManifestFile(manifestEntry.open(), metadata, minSdkVersion, pair.packageName, newPackage))) { + try (var is = new ByteArrayInputStream(modifyManifestFile(manifestEntry.open(), metadata, minSdkVersion, pair.packageName, newPackage, originalSignature))) { dstZFile.add(ANDROID_MANIFEST_XML, is); } catch (Throwable e) { throw new PatchError("Error when modifying manifest", e); @@ -422,44 +428,52 @@ public class NPatch { } } - private byte[] modifyManifestFile(InputStream is, String metadata, int minSdkVersion, String originPackage, String newPackage) throws IOException { + private byte[] modifyManifestFile(InputStream is, String metadata, int minSdkVersion, String originPackage, String newPackage, String originalSignature) throws IOException { ModificationProperty property = new ModificationProperty(); String targetPackage = (newPackage != null && !newPackage.isEmpty()) ? newPackage : originPackage; - if (overrideVersionCode) - property.addManifestAttribute(new AttributeItem(NodeValue.Manifest.VERSION_CODE, 1)); - if (minSdkVersion < 28) + if (minSdkVersion > 0) + property.addUsesSdkAttribute(new AttributeItem(NodeValue.UsesSDK.MIN_SDK_VERSION, minSdkVersion)); + else property.addUsesSdkAttribute(new AttributeItem(NodeValue.UsesSDK.MIN_SDK_VERSION, 27)); property.addApplicationAttribute(new AttributeItem(NodeValue.Application.DEBUGGABLE, debuggableFlag)); property.addApplicationAttribute(new AttributeItem("appComponentFactory", PROXY_APP_COMPONENT_FACTORY)); + property.addApplicationAttribute(new AttributeItem("isSplitRequired", false)); + if (!targetPackage.equals(originPackage)) { property.addManifestAttribute(new AttributeItem(NodeValue.Manifest.PACKAGE, targetPackage).setNamespace(null)); } - property.setPermissionMapper((type, permission) -> { - if (permission.startsWith(originPackage)) { - return permission.replaceFirst(originPackage, targetPackage); - } - if (permission.startsWith("android") - || permission.startsWith("com.android") - || permission.startsWith("com.google.android")) { - return permission; - } - return targetPackage + "_" + permission; - }); - property.setAuthorityMapper(value -> { - if (value.startsWith(originPackage)) { - return value.replaceFirst(originPackage, targetPackage); - } - return targetPackage + "_" + value; + modules.forEach(module -> { + property.addMetaData(new ModificationProperty.MetaData("xposedmodule", "true")); + property.addMetaData(new ModificationProperty.MetaData("xposeddescription", "NPatch Embed Module")); + property.addMetaData(new ModificationProperty.MetaData("xposedminversion", "93")); }); property.addMetaData(new ModificationProperty.MetaData("npatch", metadata)); + + // 注入 MicroG 偽裝簽名與權限 + if (useMicroG && originalSignature != null && !originalSignature.isEmpty()) { + try { + byte[] sigBytes = Base64.getDecoder().decode(originalSignature); + StringBuilder hex = new StringBuilder(); + for (byte b : sigBytes) { + hex.append(String.format("%02x", b)); + } + property.addMetaData(new ModificationProperty.MetaData("fake-signature", hex.toString())); + property.addUsesPermission("android.permission.FAKE_PACKAGE_SIGNATURE"); + logger.d("Added fake-signature metadata for MicroG compatibility"); + } catch (Exception e) { + logger.e("Failed to add fake-signature: " + e.getMessage()); + } + } + // TODO: replace query_all with queries -> manager if (useManager) property.addUsesPermission("android.permission.QUERY_ALL_PACKAGES"); + // 處理注入 Provider 的邏輯 if (isInjectProvider){ HashMap providerMap = new HashMap<>(); providerMap.put("name","bin.mt.file.content.MTDataFilesProvider"); diff --git a/share/java/src/main/java/org/lsposed/npatch/share/Constants.java b/share/java/src/main/java/org/lsposed/npatch/share/Constants.java index 55546cd..e50a34f 100644 --- a/share/java/src/main/java/org/lsposed/npatch/share/Constants.java +++ b/share/java/src/main/java/org/lsposed/npatch/share/Constants.java @@ -12,6 +12,7 @@ public class Constants { final static public String PATCH_FILE_SUFFIX = "-npatched.apk"; final static public String PROXY_APP_COMPONENT_FACTORY = "org.lsposed.npatch.metaloader.LSPAppComponentFactoryStub"; final static public String MANAGER_PACKAGE_NAME = "org.lsposed.npatch"; + final static public String REAL_GMS_PACKAGE_NAME = "com.google.android.gms"; final static public int MIN_ROLLING_VERSION_CODE = 400; final static public int SIGBYPASS_LV_DISABLE = 0; diff --git a/share/java/src/main/java/org/lsposed/npatch/share/PatchConfig.java b/share/java/src/main/java/org/lsposed/npatch/share/PatchConfig.java index 79ecbbd..ab242f0 100644 --- a/share/java/src/main/java/org/lsposed/npatch/share/PatchConfig.java +++ b/share/java/src/main/java/org/lsposed/npatch/share/PatchConfig.java @@ -13,6 +13,7 @@ public class PatchConfig { public final LSPConfig lspConfig; public final String managerPackageName; public final String newPackage; + public final boolean useMicroG; public PatchConfig( boolean useManager, @@ -23,7 +24,8 @@ public class PatchConfig { String appComponentFactory, boolean injectProvider, boolean outputLog, - String newPackage + String newPackage, + boolean useMicroG ) { this.useManager = useManager; this.debuggable = debuggable; @@ -36,5 +38,6 @@ public class PatchConfig { this.managerPackageName = Constants.MANAGER_PACKAGE_NAME; this.newPackage = newPackage; this.outputLog = outputLog; + this.useMicroG = useMicroG; } } \ No newline at end of file