feat(GmsCore support): Reduce amount of necessary changes and add update check (#6582)
This commit is contained in:
+349
-212
@@ -1,8 +1,5 @@
|
|||||||
package app.revanced.extension.shared;
|
package app.revanced.extension.shared;
|
||||||
|
|
||||||
import static app.revanced.extension.shared.StringRef.str;
|
|
||||||
import static app.revanced.extension.shared.requests.Route.Method.GET;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
@@ -17,46 +14,53 @@ import android.os.PowerManager;
|
|||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import app.revanced.extension.shared.requests.Requester;
|
||||||
|
import app.revanced.extension.shared.requests.Route;
|
||||||
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
|
import app.revanced.extension.shared.ui.CustomDialog;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import app.revanced.extension.shared.requests.Requester;
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
import app.revanced.extension.shared.requests.Route;
|
import static app.revanced.extension.shared.requests.Route.Method.GET;
|
||||||
import app.revanced.extension.shared.ui.CustomDialog;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class GmsCoreSupport {
|
public class GmsCoreSupport {
|
||||||
private static final String GMS_CORE_PACKAGE_NAME
|
private static GmsCore gmsCore = GmsCore.UNKNOWN;
|
||||||
= getGmsCoreVendorGroupId() + ".android.gms";
|
|
||||||
private static final Uri GMS_CORE_PROVIDER
|
|
||||||
= Uri.parse("content://" + getGmsCoreVendorGroupId() + ".android.gsf.gservices/prefix");
|
|
||||||
private static final String DONT_KILL_MY_APP_URL
|
|
||||||
= "https://dontkillmyapp.com/";
|
|
||||||
private static final Route DONT_KILL_MY_APP_MANUFACTURER_API
|
|
||||||
= new Route(GET, "/api/v2/{manufacturer}.json");
|
|
||||||
private static final String DONT_KILL_MY_APP_NAME_PARAMETER
|
|
||||||
= "?app=MicroG";
|
|
||||||
private static final String BUILD_MANUFACTURER
|
|
||||||
= Build.MANUFACTURER.toLowerCase(Locale.ROOT).replace(" ", "-");
|
|
||||||
|
|
||||||
/**
|
static {
|
||||||
* If a manufacturer specific page exists on DontKillMyApp.
|
for (GmsCore core : GmsCore.values()) {
|
||||||
*/
|
if (core.getGroupId().equals(getGmsCoreVendorGroupId())) {
|
||||||
@Nullable
|
GmsCoreSupport.gmsCore = core;
|
||||||
private static volatile Boolean DONT_KILL_MY_APP_MANUFACTURER_SUPPORTED;
|
break;
|
||||||
|
}
|
||||||
private static String getOriginalPackageName() {
|
}
|
||||||
return null; // Modified during patching.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*/
|
||||||
|
public static void checkGmsCore(Activity context) {
|
||||||
|
gmsCore.check(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getOriginalPackageName() {
|
||||||
|
return null; // Modified during patching.
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getGmsCoreVendorGroupId() {
|
||||||
|
return "app.revanced"; // Modified during patching.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return If the current package name is the same as the original unpatched app.
|
* @return If the current package name is the same as the original unpatched app.
|
||||||
* If `GmsCore support` was not included during patching, this returns true;
|
* If `GmsCore support` was not included during patching, this returns true;
|
||||||
*/
|
*/
|
||||||
public static boolean isPackageNameOriginal() {
|
public static boolean isPackageNameOriginal() {
|
||||||
String originalPackageName = getOriginalPackageName();
|
String originalPackageName = getOriginalPackageName();
|
||||||
@@ -64,203 +68,336 @@ public class GmsCoreSupport {
|
|||||||
|| originalPackageName.equals(Utils.getContext().getPackageName());
|
|| originalPackageName.equals(Utils.getContext().getPackageName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void open(String queryOrLink) {
|
private enum GmsCore {
|
||||||
Logger.printInfo(() -> "Opening link: " + queryOrLink);
|
REVANCED("app.revanced", "https://github.com/revanced/gmscore/releases/latest", () -> {
|
||||||
|
|
||||||
Intent intent;
|
|
||||||
try {
|
|
||||||
// Check if queryOrLink is a valid URL.
|
|
||||||
new URL(queryOrLink);
|
|
||||||
|
|
||||||
intent = new Intent(Intent.ACTION_VIEW, Uri.parse(queryOrLink));
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
intent = new Intent(Intent.ACTION_WEB_SEARCH);
|
|
||||||
intent.putExtra(SearchManager.QUERY, queryOrLink);
|
|
||||||
}
|
|
||||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
Utils.getContext().startActivity(intent);
|
|
||||||
|
|
||||||
// Gracefully exit, otherwise the broken app will continue to run.
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void showBatteryOptimizationDialog(Activity context,
|
|
||||||
String dialogMessageRef,
|
|
||||||
String positiveButtonTextRef,
|
|
||||||
DialogInterface.OnClickListener onPositiveClickListener) {
|
|
||||||
// Use a delay to allow the activity to finish initializing.
|
|
||||||
// Otherwise, if device is in dark mode the dialog is shown with wrong color scheme.
|
|
||||||
Utils.runOnMainThreadDelayed(() -> {
|
|
||||||
// Create the custom dialog.
|
|
||||||
Pair<Dialog, LinearLayout> dialogPair = CustomDialog.create(
|
|
||||||
context,
|
|
||||||
str("gms_core_dialog_title"), // Title.
|
|
||||||
str(dialogMessageRef), // Message.
|
|
||||||
null, // No EditText.
|
|
||||||
str(positiveButtonTextRef), // OK button text.
|
|
||||||
() -> onPositiveClickListener.onClick(null, 0), // Convert DialogInterface.OnClickListener to Runnable.
|
|
||||||
null, // No Cancel button action.
|
|
||||||
null, // No Neutral button text.
|
|
||||||
null, // No Neutral button action.
|
|
||||||
true // Dismiss dialog when onNeutralClick.
|
|
||||||
);
|
|
||||||
|
|
||||||
Dialog dialog = dialogPair.first;
|
|
||||||
|
|
||||||
// Do not set cancelable to false, to allow using back button to skip the action,
|
|
||||||
// just in case the battery change can never be satisfied.
|
|
||||||
dialog.setCancelable(true);
|
|
||||||
|
|
||||||
// Show the dialog
|
|
||||||
Utils.showDialog(context, dialog);
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Injection point.
|
|
||||||
*/
|
|
||||||
public static void checkGmsCore(Activity context) {
|
|
||||||
try {
|
|
||||||
// Verify the user has not included GmsCore for a root installation.
|
|
||||||
// GmsCore Support changes the package name, but with a mounted installation
|
|
||||||
// all manifest changes are ignored and the original package name is used.
|
|
||||||
if (isPackageNameOriginal()) {
|
|
||||||
Logger.printInfo(() -> "App is mounted with root, but GmsCore patch was included");
|
|
||||||
// Cannot use localize text here, since the app will load resources
|
|
||||||
// from the unpatched app and all patch strings are missing.
|
|
||||||
Utils.showToastLong("The 'GmsCore support' patch breaks mount installations");
|
|
||||||
|
|
||||||
// Do not exit. If the app exits before launch completes (and without
|
|
||||||
// opening another activity), then on some devices such as Pixel phone Android 10
|
|
||||||
// no toast will be shown and the app will continually relaunch
|
|
||||||
// with the appearance of a hung app.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verify GmsCore is installed.
|
|
||||||
try {
|
try {
|
||||||
PackageManager manager = context.getPackageManager();
|
|
||||||
manager.getPackageInfo(GMS_CORE_PACKAGE_NAME, PackageManager.GET_ACTIVITIES);
|
|
||||||
} catch (PackageManager.NameNotFoundException exception) {
|
|
||||||
Logger.printInfo(() -> "GmsCore was not found");
|
|
||||||
// Cannot show a dialog and must show a toast,
|
|
||||||
// because on some installations the app crashes before a dialog can be displayed.
|
|
||||||
Utils.showToastLong(str("gms_core_toast_not_installed_message"));
|
|
||||||
open(getGmsCoreDownload());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if GmsCore is whitelisted from battery optimizations.
|
|
||||||
if (isAndroidAutomotive(context)) {
|
|
||||||
// Ignore Android Automotive devices (Google built-in),
|
|
||||||
// as there is no way to disable battery optimizations.
|
|
||||||
Logger.printDebug(() -> "Device is Android Automotive");
|
|
||||||
} else if (batteryOptimizationsEnabled(context)) {
|
|
||||||
Logger.printInfo(() -> "GmsCore is not whitelisted from battery optimizations");
|
|
||||||
|
|
||||||
showBatteryOptimizationDialog(context,
|
|
||||||
"gms_core_dialog_not_whitelisted_using_battery_optimizations_message",
|
|
||||||
"gms_core_dialog_continue_text",
|
|
||||||
(dialog, id) -> openGmsCoreDisableBatteryOptimizationsIntent(context));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if GmsCore is currently running in the background.
|
|
||||||
var client = context.getContentResolver().acquireContentProviderClient(GMS_CORE_PROVIDER);
|
|
||||||
//noinspection TryFinallyCanBeTryWithResources
|
|
||||||
try {
|
|
||||||
if (client == null) {
|
|
||||||
Logger.printInfo(() -> "GmsCore is not running in the background");
|
|
||||||
checkIfDontKillMyAppSupportsManufacturer();
|
|
||||||
|
|
||||||
showBatteryOptimizationDialog(context,
|
|
||||||
"gms_core_dialog_not_whitelisted_not_allowed_in_background_message",
|
|
||||||
"gms_core_dialog_open_website_text",
|
|
||||||
(dialog, id) -> openDontKillMyApp());
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (client != null) client.close();
|
|
||||||
}
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Logger.printException(() -> "checkGmsCore failure", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("BatteryLife") // Permission is part of GmsCore
|
|
||||||
private static void openGmsCoreDisableBatteryOptimizationsIntent(Activity activity) {
|
|
||||||
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
|
|
||||||
intent.setData(Uri.fromParts("package", GMS_CORE_PACKAGE_NAME, null));
|
|
||||||
activity.startActivityForResult(intent, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkIfDontKillMyAppSupportsManufacturer() {
|
|
||||||
Utils.runOnBackgroundThread(() -> {
|
|
||||||
try {
|
|
||||||
final long start = System.currentTimeMillis();
|
|
||||||
HttpURLConnection connection = Requester.getConnectionFromRoute(
|
HttpURLConnection connection = Requester.getConnectionFromRoute(
|
||||||
DONT_KILL_MY_APP_URL, DONT_KILL_MY_APP_MANUFACTURER_API, BUILD_MANUFACTURER);
|
"https://api.github.com",
|
||||||
|
new Route(GET, "/repos/revanced/gmscore/releases/latest")
|
||||||
|
);
|
||||||
connection.setConnectTimeout(5000);
|
connection.setConnectTimeout(5000);
|
||||||
connection.setReadTimeout(5000);
|
connection.setReadTimeout(5000);
|
||||||
|
|
||||||
final boolean supported = connection.getResponseCode() == 200;
|
int responseCode = connection.getResponseCode();
|
||||||
Logger.printInfo(() -> "Manufacturer is " + (supported ? "" : "NOT ")
|
if (responseCode != 200) {
|
||||||
+ "listed on DontKillMyApp: " + BUILD_MANUFACTURER
|
Logger.printDebug(() -> "GitHub API returned status code: " + responseCode);
|
||||||
+ " fetch took: " + (System.currentTimeMillis() - start) + "ms");
|
return null;
|
||||||
DONT_KILL_MY_APP_MANUFACTURER_SUPPORTED = supported;
|
}
|
||||||
|
|
||||||
|
// Parse the response
|
||||||
|
JSONObject releaseData = Requester.parseJSONObject(connection);
|
||||||
|
String tagName = releaseData.optString("tag_name", "");
|
||||||
|
connection.disconnect();
|
||||||
|
|
||||||
|
if (tagName.isEmpty()) {
|
||||||
|
Logger.printDebug(() -> "No tag_name found in GitHub release data");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagName.startsWith("v")) tagName = tagName.substring(1);
|
||||||
|
|
||||||
|
return tagName;
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printInfo(() -> "Could not check if manufacturer is listed on DontKillMyApp: "
|
Logger.printInfo(() -> "Failed to fetch latest GmsCore version from GitHub", ex);
|
||||||
+ BUILD_MANUFACTURER, ex);
|
return null;
|
||||||
DONT_KILL_MY_APP_MANUFACTURER_SUPPORTED = null;
|
|
||||||
}
|
}
|
||||||
});
|
}),
|
||||||
}
|
UNKNOWN(getGmsCoreVendorGroupId(), getGmsCoreVendorGroupId() + "android.gms", () -> null);
|
||||||
|
|
||||||
private static void openDontKillMyApp() {
|
private static final String DONT_KILL_MY_APP_URL
|
||||||
final Boolean manufacturerSupported = DONT_KILL_MY_APP_MANUFACTURER_SUPPORTED;
|
= "https://dontkillmyapp.com/";
|
||||||
|
private static final Route DONT_KILL_MY_APP_MANUFACTURER_API
|
||||||
|
= new Route(GET, "/api/v2/{manufacturer}.json");
|
||||||
|
private static final String DONT_KILL_MY_APP_NAME_PARAMETER
|
||||||
|
= "?app=MicroG";
|
||||||
|
private static final String BUILD_MANUFACTURER
|
||||||
|
= Build.MANUFACTURER.toLowerCase(Locale.ROOT).replace(" ", "-");
|
||||||
|
|
||||||
String manufacturerPageToOpen;
|
/**
|
||||||
if (manufacturerSupported == null) {
|
* If a manufacturer specific page exists on DontKillMyApp.
|
||||||
// Fetch has not completed yet. Only happens on extremely slow internet connections
|
*/
|
||||||
// and the user spends less than 1 second reading what's on screen.
|
@Nullable
|
||||||
// Instead of waiting for the fetch (which may timeout),
|
private volatile Boolean dontKillMyAppManufacturerSupported;
|
||||||
// open the website without a vendor.
|
|
||||||
manufacturerPageToOpen = "";
|
private final String groupId;
|
||||||
} else if (manufacturerSupported) {
|
private final String packageName;
|
||||||
manufacturerPageToOpen = BUILD_MANUFACTURER;
|
private final String downloadQuery;
|
||||||
} else {
|
private final GetLatestVersion getLatestVersion;
|
||||||
// No manufacturer specific page exists. Open the general page.
|
private final Uri gmsCoreProvider;
|
||||||
manufacturerPageToOpen = "general";
|
|
||||||
|
GmsCore(String groupId, String downloadQuery, GetLatestVersion getLatestVersion) {
|
||||||
|
this.groupId = groupId;
|
||||||
|
this.packageName = groupId + ".android.gms";
|
||||||
|
this.gmsCoreProvider = Uri.parse("content://" + groupId + ".android.gsf.gservices/prefix");
|
||||||
|
|
||||||
|
this.downloadQuery = downloadQuery;
|
||||||
|
this.getLatestVersion = getLatestVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
open(DONT_KILL_MY_APP_URL + manufacturerPageToOpen + DONT_KILL_MY_APP_NAME_PARAMETER);
|
String getGroupId() {
|
||||||
}
|
return groupId;
|
||||||
|
}
|
||||||
/**
|
|
||||||
* @return If GmsCore is not whitelisted from battery optimizations.
|
void check(Activity context) {
|
||||||
*/
|
checkInstallation(context);
|
||||||
private static boolean batteryOptimizationsEnabled(Context context) {
|
checkUpdates(context);
|
||||||
//noinspection ObsoleteSdkInt
|
}
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
|
||||||
// Android 5.0 does not have battery optimization settings.
|
private void checkInstallation(Activity context) {
|
||||||
return false;
|
try {
|
||||||
|
// Verify the user has not included GmsCore for a root installation.
|
||||||
|
// GmsCore Support changes the package name, but with a mounted installation
|
||||||
|
// all manifest changes are ignored and the original package name is used.
|
||||||
|
if (isPackageNameOriginal()) {
|
||||||
|
Logger.printInfo(() -> "App is mounted with root, but GmsCore patch was included");
|
||||||
|
// Cannot use localize text here, since the app will load resources
|
||||||
|
// from the unpatched app and all patch strings are missing.
|
||||||
|
Utils.showToastLong("The 'GmsCore support' patch breaks mount installations");
|
||||||
|
|
||||||
|
// Do not exit. If the app exits before launch completes (and without
|
||||||
|
// opening another activity), then on some devices such as Pixel phone Android 10
|
||||||
|
// no toast will be shown and the app will continually relaunch
|
||||||
|
// with the appearance of a hung app.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify GmsCore is installed.
|
||||||
|
try {
|
||||||
|
PackageManager manager = context.getPackageManager();
|
||||||
|
manager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
|
||||||
|
} catch (PackageManager.NameNotFoundException exception) {
|
||||||
|
Logger.printInfo(() -> "GmsCore was not found");
|
||||||
|
// Cannot show a dialog and must show a toast,
|
||||||
|
// because on some installations the app crashes before a dialog can be displayed.
|
||||||
|
Utils.showToastLong(str("revanced_gms_core_toast_not_installed_message"));
|
||||||
|
|
||||||
|
open(downloadQuery);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if GmsCore is whitelisted from battery optimizations.
|
||||||
|
if (isAndroidAutomotive(context)) {
|
||||||
|
// Ignore Android Automotive devices (Google built-in),
|
||||||
|
// as there is no way to disable battery optimizations.
|
||||||
|
Logger.printDebug(() -> "Device is Android Automotive");
|
||||||
|
} else if (batteryOptimizationsEnabled(context)) {
|
||||||
|
Logger.printInfo(() -> "GmsCore is not whitelisted from battery optimizations");
|
||||||
|
|
||||||
|
showBatteryOptimizationDialog(context,
|
||||||
|
"revanced_gms_core_dialog_not_whitelisted_using_battery_optimizations_message",
|
||||||
|
"revanced_gms_core_dialog_continue_text",
|
||||||
|
(dialog, id) -> openGmsCoreDisableBatteryOptimizationsIntent(context));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if GmsCore is currently running in the background.
|
||||||
|
var client = context.getContentResolver().acquireContentProviderClient(gmsCoreProvider);
|
||||||
|
//noinspection TryFinallyCanBeTryWithResources
|
||||||
|
try {
|
||||||
|
if (client == null) {
|
||||||
|
Logger.printInfo(() -> "GmsCore is not running in the background");
|
||||||
|
checkIfDontKillMyAppSupportsManufacturer();
|
||||||
|
|
||||||
|
showBatteryOptimizationDialog(context,
|
||||||
|
"revanced_gms_core_dialog_not_whitelisted_not_allowed_in_background_message",
|
||||||
|
"gmsrevanced_gms_core_log_open_website_text",
|
||||||
|
(dialog, id) -> openDontKillMyApp());
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (client != null) client.close();
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "checkGmsCore failure", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkUpdates(Activity context) {
|
||||||
|
if (!BaseSettings.GMS_CORE_CHECK_UPDATES.get()) {
|
||||||
|
Logger.printDebug(() -> "GmsCore update check is disabled in settings");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils.runOnBackgroundThread(() -> {
|
||||||
|
try {
|
||||||
|
PackageManager manager = context.getPackageManager();
|
||||||
|
String installedVersion = manager.getPackageInfo(packageName, 0).versionName;
|
||||||
|
|
||||||
|
Logger.printDebug(() -> "Installed GmsCore version: " + installedVersion);
|
||||||
|
|
||||||
|
String latestVersion = getLatestVersion.get();
|
||||||
|
|
||||||
|
if (latestVersion == null || latestVersion.isEmpty()) {
|
||||||
|
Logger.printDebug(() -> "Could not get latest GmsCore version");
|
||||||
|
Utils.showToastLong(str("revanced_gms_core_toast_update_check_failed_message"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.printDebug(() -> "Latest GmsCore version on GitHub: " + latestVersion);
|
||||||
|
|
||||||
|
// Compare versions
|
||||||
|
if (!installedVersion.equals(latestVersion)) {
|
||||||
|
Logger.printInfo(() -> "GmsCore update available. Installed: " + installedVersion
|
||||||
|
+ ", Latest: " + latestVersion);
|
||||||
|
|
||||||
|
showUpdateDialog(context, installedVersion, latestVersion);
|
||||||
|
} else {
|
||||||
|
Logger.printDebug(() -> "GmsCore is up to date");
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printInfo(() -> "Could not check GmsCore updates", ex);
|
||||||
|
Utils.showToastLong(str("revanced_gms_core_toast_update_check_failed_message"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void open(String queryOrLink) {
|
||||||
|
Logger.printInfo(() -> "Opening link: " + queryOrLink);
|
||||||
|
|
||||||
|
Intent intent;
|
||||||
|
try {
|
||||||
|
// Check if queryOrLink is a valid URL.
|
||||||
|
new URL(queryOrLink);
|
||||||
|
|
||||||
|
intent = new Intent(Intent.ACTION_VIEW, Uri.parse(queryOrLink));
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
intent = new Intent(Intent.ACTION_WEB_SEARCH);
|
||||||
|
intent.putExtra(SearchManager.QUERY, queryOrLink);
|
||||||
|
}
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
Utils.getContext().startActivity(intent);
|
||||||
|
|
||||||
|
// Gracefully exit, otherwise the broken app will continue to run.
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showUpdateDialog(Activity context, String installedVersion, String latestVersion) {
|
||||||
|
// Use a delay to allow the activity to finish initializing.
|
||||||
|
// Otherwise, if device is in dark mode the dialog is shown with wrong color scheme.
|
||||||
|
Utils.runOnMainThreadDelayed(() -> {
|
||||||
|
try {
|
||||||
|
Pair<Dialog, LinearLayout> dialogPair = CustomDialog.create(
|
||||||
|
context,
|
||||||
|
str("revanced_gms_core_dialog_title"),
|
||||||
|
String.format(str("revanced_gms_core_update_available_message"), latestVersion, installedVersion),
|
||||||
|
null,
|
||||||
|
str("revanced_gms_core_dialog_open_website_text"),
|
||||||
|
() -> open(downloadQuery),
|
||||||
|
() -> {
|
||||||
|
},
|
||||||
|
str("revanced_gms_core_dialog_cancel_text"),
|
||||||
|
null,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
Dialog dialog = dialogPair.first;
|
||||||
|
dialog.setCancelable(true);
|
||||||
|
Utils.showDialog(context, dialog);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printException(() -> "Failed to show GmsCore update dialog", ex);
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void showBatteryOptimizationDialog(Activity context,
|
||||||
|
String dialogMessageRef,
|
||||||
|
String positiveButtonTextRef,
|
||||||
|
DialogInterface.OnClickListener onPositiveClickListener) {
|
||||||
|
// Use a delay to allow the activity to finish initializing.
|
||||||
|
// Otherwise, if device is in dark mode the dialog is shown with wrong color scheme.
|
||||||
|
Utils.runOnMainThreadDelayed(() -> {
|
||||||
|
// Create the custom dialog.
|
||||||
|
Pair<Dialog, LinearLayout> dialogPair = CustomDialog.create(
|
||||||
|
context,
|
||||||
|
str("revanced_gms_core_dialog_title"), // Title.
|
||||||
|
str(dialogMessageRef), // Message.
|
||||||
|
null, // No EditText.
|
||||||
|
str(positiveButtonTextRef), // OK button text.
|
||||||
|
() -> onPositiveClickListener.onClick(null, 0), // Convert DialogInterface.OnClickListener to Runnable.
|
||||||
|
null, // No Cancel button action.
|
||||||
|
null, // No Neutral button text.
|
||||||
|
null, // No Neutral button action.
|
||||||
|
true // Dismiss dialog when onNeutralClick.
|
||||||
|
);
|
||||||
|
|
||||||
|
Dialog dialog = dialogPair.first;
|
||||||
|
|
||||||
|
// Do not set cancelable to false to allow using back button to skip the action,
|
||||||
|
// just in case the battery change can never be satisfied.
|
||||||
|
dialog.setCancelable(true);
|
||||||
|
|
||||||
|
// Show the dialog
|
||||||
|
Utils.showDialog(context, dialog);
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("BatteryLife") // Permission is part of GmsCore
|
||||||
|
private void openGmsCoreDisableBatteryOptimizationsIntent(Activity activity) {
|
||||||
|
Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
|
||||||
|
intent.setData(Uri.fromParts("package", packageName, null));
|
||||||
|
activity.startActivityForResult(intent, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkIfDontKillMyAppSupportsManufacturer() {
|
||||||
|
Utils.runOnBackgroundThread(() -> {
|
||||||
|
try {
|
||||||
|
final long start = System.currentTimeMillis();
|
||||||
|
HttpURLConnection connection = Requester.getConnectionFromRoute(
|
||||||
|
DONT_KILL_MY_APP_URL, DONT_KILL_MY_APP_MANUFACTURER_API, BUILD_MANUFACTURER);
|
||||||
|
connection.setConnectTimeout(5000);
|
||||||
|
connection.setReadTimeout(5000);
|
||||||
|
|
||||||
|
final boolean supported = connection.getResponseCode() == 200;
|
||||||
|
Logger.printInfo(() -> "Manufacturer is " + (supported ? "" : "NOT ")
|
||||||
|
+ "listed on DontKillMyApp: " + BUILD_MANUFACTURER
|
||||||
|
+ " fetch took: " + (System.currentTimeMillis() - start) + "ms");
|
||||||
|
dontKillMyAppManufacturerSupported = supported;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Logger.printInfo(() -> "Could not check if manufacturer is listed on DontKillMyApp: "
|
||||||
|
+ BUILD_MANUFACTURER, ex);
|
||||||
|
dontKillMyAppManufacturerSupported = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openDontKillMyApp() {
|
||||||
|
final Boolean manufacturerSupported = dontKillMyAppManufacturerSupported;
|
||||||
|
|
||||||
|
String manufacturerPageToOpen;
|
||||||
|
if (manufacturerSupported == null) {
|
||||||
|
// Fetch has not completed yet. Only happens on extremely slow internet connections
|
||||||
|
// and the user spends less than 1 second reading what's on screen.
|
||||||
|
// Instead of waiting for the fetch (which may timeout),
|
||||||
|
// open the website without a vendor.
|
||||||
|
manufacturerPageToOpen = "";
|
||||||
|
} else if (manufacturerSupported) {
|
||||||
|
manufacturerPageToOpen = BUILD_MANUFACTURER;
|
||||||
|
} else {
|
||||||
|
// No manufacturer specific page exists. Open the general page.
|
||||||
|
manufacturerPageToOpen = "general";
|
||||||
|
}
|
||||||
|
|
||||||
|
open(DONT_KILL_MY_APP_URL + manufacturerPageToOpen + DONT_KILL_MY_APP_NAME_PARAMETER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return If GmsCore is not whitelisted from battery optimizations.
|
||||||
|
*/
|
||||||
|
private boolean batteryOptimizationsEnabled(Context context) {
|
||||||
|
//noinspection ObsoleteSdkInt
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||||
|
// Android 5.0 does not have battery optimization settings.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||||
|
return !powerManager.isIgnoringBatteryOptimizations(packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAndroidAutomotive(Context context) {
|
||||||
|
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
|
||||||
}
|
}
|
||||||
var powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
|
||||||
return !powerManager.isIgnoringBatteryOptimizations(GMS_CORE_PACKAGE_NAME);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isAndroidAutomotive(Context context) {
|
@FunctionalInterface
|
||||||
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
|
private interface GetLatestVersion {
|
||||||
}
|
String get();
|
||||||
|
|
||||||
private static String getGmsCoreDownload() {
|
|
||||||
final var vendorGroupId = getGmsCoreVendorGroupId();
|
|
||||||
//noinspection SwitchStatementWithTooFewBranches
|
|
||||||
return switch (vendorGroupId) {
|
|
||||||
case "app.revanced" -> "https://github.com/revanced/gmscore/releases/latest";
|
|
||||||
default -> vendorGroupId + ".android.gms";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getGmsCoreVendorGroupId() {
|
|
||||||
return "app.revanced"; // Modified during patching.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -28,6 +28,8 @@ public class BaseSettings {
|
|||||||
public static final BooleanSetting SETTINGS_SEARCH_HISTORY = new BooleanSetting("revanced_settings_search_history", TRUE, true);
|
public static final BooleanSetting SETTINGS_SEARCH_HISTORY = new BooleanSetting("revanced_settings_search_history", TRUE, true);
|
||||||
public static final StringSetting SETTINGS_SEARCH_ENTRIES = new StringSetting("revanced_settings_search_entries", "");
|
public static final StringSetting SETTINGS_SEARCH_ENTRIES = new StringSetting("revanced_settings_search_entries", "");
|
||||||
|
|
||||||
|
public static final BooleanSetting GMS_CORE_CHECK_UPDATES = new BooleanSetting("revanced_gms_core_check_updates", true, true);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Settings shared by YouTube and YouTube Music.
|
// Settings shared by YouTube and YouTube Music.
|
||||||
//
|
//
|
||||||
|
|||||||
-1
@@ -27,5 +27,4 @@ private fun gmsCoreSupportResourcePatch(
|
|||||||
toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME,
|
toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME,
|
||||||
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a666",
|
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a666",
|
||||||
gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
|
gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
|
||||||
addStringResources = false,
|
|
||||||
)
|
)
|
||||||
|
|||||||
-1
@@ -22,7 +22,6 @@ private fun gmsCoreSupportResourcePatch(
|
|||||||
) = app.revanced.patches.shared.misc.gms.gmsCoreSupportResourcePatch(
|
) = app.revanced.patches.shared.misc.gms.gmsCoreSupportResourcePatch(
|
||||||
fromPackageName = PHOTOS_PACKAGE_NAME,
|
fromPackageName = PHOTOS_PACKAGE_NAME,
|
||||||
toPackageName = REVANCED_PHOTOS_PACKAGE_NAME,
|
toPackageName = REVANCED_PHOTOS_PACKAGE_NAME,
|
||||||
addStringResources = false,
|
|
||||||
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a600",
|
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a600",
|
||||||
gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
|
gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
|
||||||
)
|
)
|
||||||
|
|||||||
+20
-12
@@ -4,15 +4,17 @@ import app.revanced.patcher.patch.Option
|
|||||||
import app.revanced.patches.all.misc.resources.addResources
|
import app.revanced.patches.all.misc.resources.addResources
|
||||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||||
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
|
||||||
|
import app.revanced.patches.music.misc.fileprovider.fileProviderPatch
|
||||||
import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME
|
import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME
|
||||||
import app.revanced.patches.music.misc.gms.Constants.REVANCED_MUSIC_PACKAGE_NAME
|
import app.revanced.patches.music.misc.gms.Constants.REVANCED_MUSIC_PACKAGE_NAME
|
||||||
import app.revanced.patches.music.misc.settings.PreferenceScreen
|
import app.revanced.patches.music.misc.settings.PreferenceScreen
|
||||||
import app.revanced.patches.music.misc.settings.settingsPatch
|
import app.revanced.patches.music.misc.settings.settingsPatch
|
||||||
import app.revanced.patches.music.misc.spoof.spoofVideoStreamsPatch
|
import app.revanced.patches.music.misc.spoof.spoofVideoStreamsPatch
|
||||||
import app.revanced.patches.music.misc.fileprovider.fileProviderPatch
|
|
||||||
import app.revanced.patches.shared.castContextFetchFingerprint
|
import app.revanced.patches.shared.castContextFetchFingerprint
|
||||||
import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch
|
import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch
|
||||||
import app.revanced.patches.shared.misc.settings.preference.IntentPreference
|
import app.revanced.patches.shared.misc.settings.preference.IntentPreference
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.shared.primeMethodFingerprint
|
import app.revanced.patches.shared.primeMethodFingerprint
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
@@ -32,8 +34,8 @@ val gmsCoreSupportPatch = gmsCoreSupportPatch(
|
|||||||
compatibleWith(
|
compatibleWith(
|
||||||
MUSIC_PACKAGE_NAME(
|
MUSIC_PACKAGE_NAME(
|
||||||
"7.29.52",
|
"7.29.52",
|
||||||
"8.10.52"
|
"8.10.52",
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,21 +52,27 @@ private fun gmsCoreSupportResourcePatch(
|
|||||||
val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
|
val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
|
||||||
|
|
||||||
PreferenceScreen.MISC.addPreferences(
|
PreferenceScreen.MISC.addPreferences(
|
||||||
IntentPreference(
|
PreferenceScreenPreference(
|
||||||
"microg_settings",
|
"revanced_gms_core_screen",
|
||||||
intent = IntentPreference.Intent("", "org.microg.gms.ui.SettingsActivity") {
|
preferences = setOf(
|
||||||
"$gmsCoreVendorGroupId.android.gms"
|
SwitchPreference("revanced_gms_core_check_updates"),
|
||||||
}
|
IntentPreference(
|
||||||
)
|
"revanced_gms_core_settings",
|
||||||
|
intent = IntentPreference.Intent("", "org.microg.gms.ui.SettingsActivity") {
|
||||||
|
"$gmsCoreVendorGroupId.android.gms"
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
) {
|
) {
|
||||||
dependsOn(
|
dependsOn(
|
||||||
addResourcesPatch,
|
addResourcesPatch,
|
||||||
settingsPatch,
|
settingsPatch,
|
||||||
fileProviderPatch(
|
fileProviderPatch(
|
||||||
MUSIC_PACKAGE_NAME,
|
MUSIC_PACKAGE_NAME,
|
||||||
REVANCED_MUSIC_PACKAGE_NAME
|
REVANCED_MUSIC_PACKAGE_NAME,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ internal val serviceCheckFingerprint = fingerprint {
|
|||||||
strings("Google Play Services not available")
|
strings("Google Play Services not available")
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val gmsCoreSupportFingerprint = fingerprint {
|
internal val getGmsCoreVendorGroupIdFingerprint = fingerprint {
|
||||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||||
returns("Ljava/lang/String;")
|
returns("Ljava/lang/String;")
|
||||||
parameters()
|
parameters()
|
||||||
|
|||||||
+166
-452
@@ -2,26 +2,18 @@ package app.revanced.patches.shared.misc.gms
|
|||||||
|
|
||||||
import app.revanced.patcher.Fingerprint
|
import app.revanced.patcher.Fingerprint
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.instructions
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
import app.revanced.patcher.patch.BytecodePatchBuilder
|
import app.revanced.patcher.patch.*
|
||||||
import app.revanced.patcher.patch.BytecodePatchContext
|
|
||||||
import app.revanced.patcher.patch.Option
|
|
||||||
import app.revanced.patcher.patch.Patch
|
|
||||||
import app.revanced.patcher.patch.ResourcePatchBuilder
|
|
||||||
import app.revanced.patcher.patch.ResourcePatchContext
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
|
||||||
import app.revanced.patcher.patch.resourcePatch
|
|
||||||
import app.revanced.patcher.patch.stringOption
|
|
||||||
import app.revanced.patches.all.misc.packagename.changePackageNamePatch
|
import app.revanced.patches.all.misc.packagename.changePackageNamePatch
|
||||||
import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName
|
import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName
|
||||||
import app.revanced.patches.all.misc.resources.addResources
|
import app.revanced.patches.all.misc.resources.addResources
|
||||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
||||||
import app.revanced.patches.shared.misc.gms.Constants.ACTIONS
|
import app.revanced.patches.shared.misc.gms.Constants.APP_AUTHORITIES
|
||||||
import app.revanced.patches.shared.misc.gms.Constants.AUTHORITIES
|
import app.revanced.patches.shared.misc.gms.Constants.APP_PERMISSIONS
|
||||||
import app.revanced.patches.shared.misc.gms.Constants.PERMISSIONS
|
import app.revanced.patches.shared.misc.gms.Constants.GMS_AUTHORITIES
|
||||||
import app.revanced.util.getReference
|
import app.revanced.patches.shared.misc.gms.Constants.GMS_PERMISSIONS
|
||||||
import app.revanced.util.returnEarly
|
import app.revanced.util.*
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
|
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
@@ -29,8 +21,7 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21c
|
|||||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference
|
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference
|
||||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||||
import org.w3c.dom.Element
|
import java.net.URI
|
||||||
import org.w3c.dom.Node
|
|
||||||
|
|
||||||
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/GmsCoreSupport;"
|
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/GmsCoreSupport;"
|
||||||
|
|
||||||
@@ -69,10 +60,7 @@ fun gmsCoreSupportPatch(
|
|||||||
val gmsCoreVendorGroupIdOption = stringOption(
|
val gmsCoreVendorGroupIdOption = stringOption(
|
||||||
key = "gmsCoreVendorGroupId",
|
key = "gmsCoreVendorGroupId",
|
||||||
default = "app.revanced",
|
default = "app.revanced",
|
||||||
values =
|
values = mapOf("ReVanced" to "app.revanced"),
|
||||||
mapOf(
|
|
||||||
"ReVanced" to "app.revanced",
|
|
||||||
),
|
|
||||||
title = "GmsCore vendor group ID",
|
title = "GmsCore vendor group ID",
|
||||||
description = "The vendor's group ID for GmsCore.",
|
description = "The vendor's group ID for GmsCore.",
|
||||||
required = true,
|
required = true,
|
||||||
@@ -84,9 +72,9 @@ fun gmsCoreSupportPatch(
|
|||||||
extensionPatch,
|
extensionPatch,
|
||||||
)
|
)
|
||||||
|
|
||||||
val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
|
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
|
val gmsCoreVendorGroupId = gmsCoreVendorGroupIdOption.value!!
|
||||||
|
|
||||||
fun transformStringReferences(transform: (str: String) -> String?) = classes.forEach {
|
fun transformStringReferences(transform: (str: String) -> String?) = classes.forEach {
|
||||||
val mutableClass by lazy {
|
val mutableClass by lazy {
|
||||||
proxy(it).mutableClass
|
proxy(it).mutableClass
|
||||||
@@ -118,80 +106,54 @@ fun gmsCoreSupportPatch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// region Collection of transformations that are applied to all strings.
|
fun transformPackages(string: String): String? = when (string) {
|
||||||
|
|
||||||
fun commonTransform(referencedString: String): String? = when (referencedString) {
|
|
||||||
"com.google",
|
"com.google",
|
||||||
"com.google.android.gms",
|
"com.google.android.gms",
|
||||||
in PERMISSIONS,
|
in GMS_PERMISSIONS,
|
||||||
in ACTIONS,
|
in GMS_AUTHORITIES,
|
||||||
in AUTHORITIES,
|
-> if (string.startsWith("com.google")) {
|
||||||
-> referencedString.replace("com.google", gmsCoreVendorGroupId!!)
|
string.replace("com.google", gmsCoreVendorGroupId)
|
||||||
|
} else {
|
||||||
|
"$gmsCoreVendorGroupId.$string"
|
||||||
|
}
|
||||||
|
|
||||||
|
in APP_PERMISSIONS,
|
||||||
|
in APP_AUTHORITIES,
|
||||||
|
-> "$toPackageName.$string"
|
||||||
|
|
||||||
// No vendor prefix for whatever reason...
|
|
||||||
"subscribedfeeds" -> "$gmsCoreVendorGroupId.subscribedfeeds"
|
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun contentUrisTransform(str: String): String? {
|
fun transformContentUrlAuthority(string: String) = if (!string.startsWith("content://")) {
|
||||||
// only when content:// uri
|
null
|
||||||
if (str.startsWith("content://")) {
|
} else {
|
||||||
// check if matches any authority
|
runCatching { URI.create(string) }.map {
|
||||||
for (authority in AUTHORITIES) {
|
when (it.authority) {
|
||||||
val uriPrefix = "content://$authority"
|
in GMS_AUTHORITIES ->
|
||||||
if (str.startsWith(uriPrefix)) {
|
if (it.authority.startsWith("com.google")) {
|
||||||
return str.replace(
|
string.replace("com.google", gmsCoreVendorGroupId)
|
||||||
uriPrefix,
|
} else {
|
||||||
"content://${authority.replace("com.google", gmsCoreVendorGroupId!!)}",
|
string.replace(
|
||||||
)
|
it.authority,
|
||||||
}
|
"$gmsCoreVendorGroupId.${it.authority}",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
in APP_AUTHORITIES ->
|
||||||
|
string.replace(it.authority, "$toPackageName.${it.authority}")
|
||||||
|
|
||||||
|
else -> null
|
||||||
}
|
}
|
||||||
|
}.getOrNull()
|
||||||
// gms also has a 'subscribedfeeds' authority, check for that one too
|
|
||||||
val subFeedsUriPrefix = "content://subscribedfeeds"
|
|
||||||
if (str.startsWith(subFeedsUriPrefix)) {
|
|
||||||
return str.replace(subFeedsUriPrefix, "content://$gmsCoreVendorGroupId.subscribedfeeds")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun packageNameTransform(fromPackageName: String, toPackageName: String): (String) -> String? = { string ->
|
|
||||||
when (string) {
|
|
||||||
"$fromPackageName.SuggestionProvider",
|
|
||||||
"$fromPackageName.fileprovider",
|
|
||||||
-> string.replace(fromPackageName, toPackageName)
|
|
||||||
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun transformPrimeMethod(packageName: String) {
|
|
||||||
primeMethodFingerprint!!.method.apply {
|
|
||||||
var register = 2
|
|
||||||
|
|
||||||
val index = instructions.indexOfFirst {
|
|
||||||
if (it.getReference<StringReference>()?.string != fromPackageName) return@indexOfFirst false
|
|
||||||
|
|
||||||
register = (it as OneRegisterInstruction).registerA
|
|
||||||
return@indexOfFirst true
|
|
||||||
}
|
|
||||||
|
|
||||||
replaceInstruction(index, "const-string v$register, \"$packageName\"")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
val packageName = setOrGetFallbackPackageName(toPackageName)
|
val packageName = setOrGetFallbackPackageName(toPackageName)
|
||||||
|
|
||||||
// Transform all strings using all provided transforms, first match wins.
|
|
||||||
val transformations = arrayOf(
|
val transformations = arrayOf(
|
||||||
::commonTransform,
|
::transformPackages,
|
||||||
::contentUrisTransform,
|
::transformContentUrlAuthority,
|
||||||
packageNameTransform(fromPackageName, packageName),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
transformStringReferences transform@{ string ->
|
transformStringReferences transform@{ string ->
|
||||||
transformations.forEach { transform ->
|
transformations.forEach { transform ->
|
||||||
transform(string)?.let { transformedString -> return@transform transformedString }
|
transform(string)?.let { transformedString -> return@transform transformedString }
|
||||||
@@ -201,16 +163,26 @@ fun gmsCoreSupportPatch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Specific method that needs to be patched.
|
// Specific method that needs to be patched.
|
||||||
primeMethodFingerprint?.let { transformPrimeMethod(packageName) }
|
if (primeMethodFingerprint?.methodOrNull != null) {
|
||||||
|
val primeMethod = primeMethodFingerprint.method
|
||||||
|
|
||||||
|
val index = primeMethod.indexOfFirstInstruction {
|
||||||
|
getReference<StringReference>()?.string == fromPackageName
|
||||||
|
}
|
||||||
|
val register = primeMethod.getInstruction<OneRegisterInstruction>(index).registerA
|
||||||
|
|
||||||
|
primeMethod.replaceInstruction(
|
||||||
|
index,
|
||||||
|
"const-string v$register, \"$packageName\"",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Return these methods early to prevent the app from crashing.
|
// Return these methods early to prevent the app from crashing.
|
||||||
earlyReturnFingerprints.forEach { it.method.returnEarly() }
|
earlyReturnFingerprints.forEach { it.method.returnEarly() }
|
||||||
serviceCheckFingerprint.method.returnEarly()
|
serviceCheckFingerprint.method.returnEarly()
|
||||||
|
|
||||||
// Google Play Utility is not present in all apps, so we need to check if it's present.
|
// Google Play Utility is not present in all apps, so we need to check if it's present.
|
||||||
if (googlePlayUtilityFingerprint.methodOrNull != null) {
|
googlePlayUtilityFingerprint.methodOrNull?.returnEarly(0)
|
||||||
googlePlayUtilityFingerprint.method.returnEarly(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set original and patched package names for extension to use.
|
// Set original and patched package names for extension to use.
|
||||||
originalPackageNameExtensionFingerprint.method.returnEarly(fromPackageName)
|
originalPackageNameExtensionFingerprint.method.returnEarly(fromPackageName)
|
||||||
@@ -219,11 +191,11 @@ fun gmsCoreSupportPatch(
|
|||||||
mainActivityOnCreateFingerprint.method.addInstruction(
|
mainActivityOnCreateFingerprint.method.addInstruction(
|
||||||
0,
|
0,
|
||||||
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->" +
|
"invoke-static/range { p0 .. p0 }, $EXTENSION_CLASS_DESCRIPTOR->" +
|
||||||
"checkGmsCore(Landroid/app/Activity;)V"
|
"checkGmsCore(Landroid/app/Activity;)V",
|
||||||
)
|
)
|
||||||
|
|
||||||
// Change the vendor of GmsCore in the extension.
|
// Change the vendor of GmsCore in the extension.
|
||||||
gmsCoreSupportFingerprint.method.returnEarly(gmsCoreVendorGroupId!!)
|
getGmsCoreVendorGroupIdFingerprint.method.returnEarly(gmsCoreVendorGroupId)
|
||||||
|
|
||||||
executeBlock()
|
executeBlock()
|
||||||
}
|
}
|
||||||
@@ -231,274 +203,6 @@ fun gmsCoreSupportPatch(
|
|||||||
block()
|
block()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A collection of permissions, intents and content provider authorities
|
|
||||||
* that are present in GmsCore which need to be transformed.
|
|
||||||
*/
|
|
||||||
private object Constants {
|
|
||||||
/**
|
|
||||||
* All permissions.
|
|
||||||
*/
|
|
||||||
val PERMISSIONS = setOf(
|
|
||||||
"com.google.android.c2dm.permission.RECEIVE",
|
|
||||||
"com.google.android.c2dm.permission.SEND",
|
|
||||||
"com.google.android.gms.auth.api.phone.permission.SEND",
|
|
||||||
"com.google.android.gms.permission.AD_ID",
|
|
||||||
"com.google.android.gms.permission.AD_ID_NOTIFICATION",
|
|
||||||
"com.google.android.gms.permission.CAR_FUEL",
|
|
||||||
"com.google.android.gms.permission.CAR_INFORMATION",
|
|
||||||
"com.google.android.gms.permission.CAR_MILEAGE",
|
|
||||||
"com.google.android.gms.permission.CAR_SPEED",
|
|
||||||
"com.google.android.gms.permission.CAR_VENDOR_EXTENSION",
|
|
||||||
"com.google.android.googleapps.permission.GOOGLE_AUTH",
|
|
||||||
"com.google.android.googleapps.permission.GOOGLE_AUTH.cp",
|
|
||||||
"com.google.android.googleapps.permission.GOOGLE_AUTH.local",
|
|
||||||
"com.google.android.googleapps.permission.GOOGLE_AUTH.mail",
|
|
||||||
"com.google.android.googleapps.permission.GOOGLE_AUTH.writely",
|
|
||||||
"com.google.android.gtalkservice.permission.GTALK_SERVICE",
|
|
||||||
"com.google.android.providers.gsf.permission.READ_GSERVICES",
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All intent actions.
|
|
||||||
*/
|
|
||||||
val ACTIONS = setOf(
|
|
||||||
"com.google.android.c2dm.intent.RECEIVE",
|
|
||||||
"com.google.android.c2dm.intent.REGISTER",
|
|
||||||
"com.google.android.c2dm.intent.REGISTRATION",
|
|
||||||
"com.google.android.c2dm.intent.UNREGISTER",
|
|
||||||
"com.google.android.contextmanager.service.ContextManagerService.START",
|
|
||||||
"com.google.android.gcm.intent.SEND",
|
|
||||||
"com.google.android.gms.accounts.ACCOUNT_SERVICE",
|
|
||||||
"com.google.android.gms.accountsettings.ACCOUNT_PREFERENCES_SETTINGS",
|
|
||||||
"com.google.android.gms.accountsettings.action.BROWSE_SETTINGS",
|
|
||||||
"com.google.android.gms.accountsettings.action.VIEW_SETTINGS",
|
|
||||||
"com.google.android.gms.accountsettings.MY_ACCOUNT",
|
|
||||||
"com.google.android.gms.accountsettings.PRIVACY_SETTINGS",
|
|
||||||
"com.google.android.gms.accountsettings.SECURITY_SETTINGS",
|
|
||||||
"com.google.android.gms.ads.gservice.START",
|
|
||||||
"com.google.android.gms.ads.identifier.service.EVENT_ATTESTATION",
|
|
||||||
"com.google.android.gms.ads.service.CACHE",
|
|
||||||
"com.google.android.gms.ads.service.CONSENT_LOOKUP",
|
|
||||||
"com.google.android.gms.ads.service.HTTP",
|
|
||||||
"com.google.android.gms.analytics.service.START",
|
|
||||||
"com.google.android.gms.app.settings.GoogleSettingsLink",
|
|
||||||
"com.google.android.gms.appstate.service.START",
|
|
||||||
"com.google.android.gms.appusage.service.START",
|
|
||||||
"com.google.android.gms.asterism.service.START",
|
|
||||||
"com.google.android.gms.audiomodem.service.AudioModemService.START",
|
|
||||||
"com.google.android.gms.audit.service.START",
|
|
||||||
"com.google.android.gms.auth.account.authapi.START",
|
|
||||||
"com.google.android.gms.auth.account.authenticator.auto.service.START",
|
|
||||||
"com.google.android.gms.auth.account.authenticator.chromeos.START",
|
|
||||||
"com.google.android.gms.auth.account.authenticator.tv.service.START",
|
|
||||||
"com.google.android.gms.auth.account.data.service.START",
|
|
||||||
"com.google.android.gms.auth.api.credentials.PICKER",
|
|
||||||
"com.google.android.gms.auth.api.credentials.service.START",
|
|
||||||
"com.google.android.gms.auth.api.identity.service.authorization.START",
|
|
||||||
"com.google.android.gms.auth.api.identity.service.credentialsaving.START",
|
|
||||||
"com.google.android.gms.auth.api.identity.service.signin.START",
|
|
||||||
"com.google.android.gms.auth.api.phone.service.InternalService.START",
|
|
||||||
"com.google.android.gms.auth.api.signin.service.START",
|
|
||||||
"com.google.android.gms.auth.be.appcert.AppCertService",
|
|
||||||
"com.google.android.gms.auth.blockstore.service.START",
|
|
||||||
"com.google.android.gms.auth.config.service.START",
|
|
||||||
"com.google.android.gms.auth.cryptauth.cryptauthservice.START",
|
|
||||||
"com.google.android.gms.auth.GOOGLE_SIGN_IN",
|
|
||||||
"com.google.android.gms.auth.login.LOGIN",
|
|
||||||
"com.google.android.gms.auth.proximity.devicesyncservice.START",
|
|
||||||
"com.google.android.gms.auth.proximity.securechannelservice.START",
|
|
||||||
"com.google.android.gms.auth.proximity.START",
|
|
||||||
"com.google.android.gms.auth.service.START",
|
|
||||||
"com.google.android.gms.backup.ACTION_BACKUP_SETTINGS",
|
|
||||||
"com.google.android.gms.backup.G1_BACKUP",
|
|
||||||
"com.google.android.gms.backup.G1_RESTORE",
|
|
||||||
"com.google.android.gms.backup.GMS_MODULE_RESTORE",
|
|
||||||
"com.google.android.gms.beacon.internal.IBleService.START",
|
|
||||||
"com.google.android.gms.car.service.START",
|
|
||||||
"com.google.android.gms.carrierauth.service.START",
|
|
||||||
"com.google.android.gms.cast.firstparty.START",
|
|
||||||
"com.google.android.gms.cast.remote_display.service.START",
|
|
||||||
"com.google.android.gms.cast.service.BIND_CAST_DEVICE_CONTROLLER_SERVICE",
|
|
||||||
"com.google.android.gms.cast_mirroring.service.START",
|
|
||||||
"com.google.android.gms.checkin.BIND_TO_SERVICE",
|
|
||||||
"com.google.android.gms.chromesync.service.START",
|
|
||||||
"com.google.android.gms.clearcut.service.START",
|
|
||||||
"com.google.android.gms.common.account.CHOOSE_ACCOUNT",
|
|
||||||
"com.google.android.gms.common.download.START",
|
|
||||||
"com.google.android.gms.common.service.START",
|
|
||||||
"com.google.android.gms.common.telemetry.service.START",
|
|
||||||
"com.google.android.gms.config.START",
|
|
||||||
"com.google.android.gms.constellation.service.START",
|
|
||||||
"com.google.android.gms.credential.manager.service.firstparty.START",
|
|
||||||
"com.google.android.gms.deviceconnection.service.START",
|
|
||||||
"com.google.android.gms.drive.ApiService.RESET_AFTER_BOOT",
|
|
||||||
"com.google.android.gms.drive.ApiService.START",
|
|
||||||
"com.google.android.gms.drive.ApiService.STOP",
|
|
||||||
"com.google.android.gms.droidguard.service.INIT",
|
|
||||||
"com.google.android.gms.droidguard.service.PING",
|
|
||||||
"com.google.android.gms.droidguard.service.START",
|
|
||||||
"com.google.android.gms.enterprise.loader.service.START",
|
|
||||||
"com.google.android.gms.facs.cache.service.START",
|
|
||||||
"com.google.android.gms.facs.internal.service.START",
|
|
||||||
"com.google.android.gms.feedback.internal.IFeedbackService",
|
|
||||||
"com.google.android.gms.fido.credentialstore.internal_service.START",
|
|
||||||
"com.google.android.gms.fido.fido2.privileged.START",
|
|
||||||
"com.google.android.gms.fido.fido2.regular.START",
|
|
||||||
"com.google.android.gms.fido.fido2.zeroparty.START",
|
|
||||||
"com.google.android.gms.fido.sourcedevice.service.START",
|
|
||||||
"com.google.android.gms.fido.targetdevice.internal_service.START",
|
|
||||||
"com.google.android.gms.fido.u2f.privileged.START",
|
|
||||||
"com.google.android.gms.fido.u2f.thirdparty.START",
|
|
||||||
"com.google.android.gms.fido.u2f.zeroparty.START",
|
|
||||||
"com.google.android.gms.fitness.BleApi",
|
|
||||||
"com.google.android.gms.fitness.ConfigApi",
|
|
||||||
"com.google.android.gms.fitness.GoalsApi",
|
|
||||||
"com.google.android.gms.fitness.GoogleFitnessService.START",
|
|
||||||
"com.google.android.gms.fitness.HistoryApi",
|
|
||||||
"com.google.android.gms.fitness.InternalApi",
|
|
||||||
"com.google.android.gms.fitness.RecordingApi",
|
|
||||||
"com.google.android.gms.fitness.SensorsApi",
|
|
||||||
"com.google.android.gms.fitness.SessionsApi",
|
|
||||||
"com.google.android.gms.fonts.service.START",
|
|
||||||
"com.google.android.gms.freighter.service.START",
|
|
||||||
"com.google.android.gms.games.internal.connect.service.START",
|
|
||||||
"com.google.android.gms.games.PLAY_GAMES_UPGRADE",
|
|
||||||
"com.google.android.gms.games.service.START",
|
|
||||||
"com.google.android.gms.gass.START",
|
|
||||||
"com.google.android.gms.gmscompliance.service.START",
|
|
||||||
"com.google.android.gms.googlehelp.HELP",
|
|
||||||
"com.google.android.gms.googlehelp.service.GoogleHelpService.START",
|
|
||||||
"com.google.android.gms.growth.service.START",
|
|
||||||
"com.google.android.gms.herrevad.services.LightweightNetworkQualityAndroidService.START",
|
|
||||||
"com.google.android.gms.icing.INDEX_SERVICE",
|
|
||||||
"com.google.android.gms.icing.LIGHTWEIGHT_INDEX_SERVICE",
|
|
||||||
"com.google.android.gms.identity.service.BIND",
|
|
||||||
"com.google.android.gms.inappreach.service.START",
|
|
||||||
"com.google.android.gms.instantapps.START",
|
|
||||||
"com.google.android.gms.kids.service.START",
|
|
||||||
"com.google.android.gms.languageprofile.service.START",
|
|
||||||
"com.google.android.gms.learning.internal.dynamitesupport.START",
|
|
||||||
"com.google.android.gms.learning.intservice.START",
|
|
||||||
"com.google.android.gms.learning.predictor.START",
|
|
||||||
"com.google.android.gms.learning.trainer.START",
|
|
||||||
"com.google.android.gms.learning.training.background.START",
|
|
||||||
"com.google.android.gms.location.places.GeoDataApi",
|
|
||||||
"com.google.android.gms.location.places.PlaceDetectionApi",
|
|
||||||
"com.google.android.gms.location.places.PlacesApi",
|
|
||||||
"com.google.android.gms.location.reporting.service.START",
|
|
||||||
"com.google.android.gms.location.settings.LOCATION_HISTORY",
|
|
||||||
"com.google.android.gms.location.settings.LOCATION_REPORTING_SETTINGS",
|
|
||||||
"com.google.android.gms.locationsharing.api.START",
|
|
||||||
"com.google.android.gms.locationsharingreporter.service.START",
|
|
||||||
"com.google.android.gms.lockbox.service.START",
|
|
||||||
"com.google.android.gms.matchstick.lighter.service.START",
|
|
||||||
"com.google.android.gms.mdm.services.DeviceManagerApiService.START",
|
|
||||||
"com.google.android.gms.mdm.services.START",
|
|
||||||
"com.google.android.gms.mdns.service.START",
|
|
||||||
"com.google.android.gms.measurement.START",
|
|
||||||
"com.google.android.gms.nearby.bootstrap.service.NearbyBootstrapService.START",
|
|
||||||
"com.google.android.gms.nearby.connection.service.START",
|
|
||||||
"com.google.android.gms.nearby.fastpair.START",
|
|
||||||
"com.google.android.gms.nearby.messages.service.NearbyMessagesService.START",
|
|
||||||
"com.google.android.gms.nearby.sharing.service.NearbySharingService.START",
|
|
||||||
"com.google.android.gms.nearby.sharing.START_SERVICE",
|
|
||||||
"com.google.android.gms.notifications.service.START",
|
|
||||||
"com.google.android.gms.ocr.service.internal.START",
|
|
||||||
"com.google.android.gms.ocr.service.START",
|
|
||||||
"com.google.android.gms.oss.licenses.service.START",
|
|
||||||
"com.google.android.gms.payse.service.BIND",
|
|
||||||
"com.google.android.gms.people.contactssync.service.START",
|
|
||||||
"com.google.android.gms.people.service.START",
|
|
||||||
"com.google.android.gms.phenotype.service.START",
|
|
||||||
"com.google.android.gms.photos.autobackup.service.START",
|
|
||||||
"com.google.android.gms.playlog.service.START",
|
|
||||||
"com.google.android.gms.plus.service.default.INTENT",
|
|
||||||
"com.google.android.gms.plus.service.image.INTENT",
|
|
||||||
"com.google.android.gms.plus.service.internal.START",
|
|
||||||
"com.google.android.gms.plus.service.START",
|
|
||||||
"com.google.android.gms.potokens.service.START",
|
|
||||||
"com.google.android.gms.pseudonymous.service.START",
|
|
||||||
"com.google.android.gms.rcs.START",
|
|
||||||
"com.google.android.gms.reminders.service.START",
|
|
||||||
"com.google.android.gms.romanesco.MODULE_BACKUP_AGENT",
|
|
||||||
"com.google.android.gms.romanesco.service.START",
|
|
||||||
"com.google.android.gms.safetynet.service.START",
|
|
||||||
"com.google.android.gms.scheduler.ACTION_PROXY_SCHEDULE",
|
|
||||||
"com.google.android.gms.search.service.SEARCH_AUTH_START",
|
|
||||||
"com.google.android.gms.semanticlocation.service.START_ODLH",
|
|
||||||
"com.google.android.gms.sesame.service.BIND",
|
|
||||||
"com.google.android.gms.settings.EXPOSURE_NOTIFICATION_SETTINGS",
|
|
||||||
"com.google.android.gms.setup.auth.SecondDeviceAuth.START",
|
|
||||||
"com.google.android.gms.signin.service.START",
|
|
||||||
"com.google.android.gms.smartdevice.d2d.SourceDeviceService.START",
|
|
||||||
"com.google.android.gms.smartdevice.d2d.TargetDeviceService.START",
|
|
||||||
"com.google.android.gms.smartdevice.directtransfer.SourceDirectTransferService.START",
|
|
||||||
"com.google.android.gms.smartdevice.directtransfer.TargetDirectTransferService.START",
|
|
||||||
"com.google.android.gms.smartdevice.postsetup.PostSetupService.START",
|
|
||||||
"com.google.android.gms.smartdevice.setup.accounts.AccountsService.START",
|
|
||||||
"com.google.android.gms.smartdevice.wifi.START_WIFI_HELPER_SERVICE",
|
|
||||||
"com.google.android.gms.social.location.activity.service.START",
|
|
||||||
"com.google.android.gms.speech.service.START",
|
|
||||||
"com.google.android.gms.statementservice.EXECUTE",
|
|
||||||
"com.google.android.gms.stats.ACTION_UPLOAD_DROPBOX_ENTRIES",
|
|
||||||
"com.google.android.gms.tapandpay.service.BIND",
|
|
||||||
"com.google.android.gms.telephonyspam.service.START",
|
|
||||||
"com.google.android.gms.testsupport.service.START",
|
|
||||||
"com.google.android.gms.thunderbird.service.START",
|
|
||||||
"com.google.android.gms.trustagent.BridgeApi.START",
|
|
||||||
"com.google.android.gms.trustagent.StateApi.START",
|
|
||||||
"com.google.android.gms.trustagent.trustlet.trustletmanagerservice.BIND",
|
|
||||||
"com.google.android.gms.trustlet.bluetooth.service.BIND",
|
|
||||||
"com.google.android.gms.trustlet.connectionlessble.service.BIND",
|
|
||||||
"com.google.android.gms.trustlet.face.service.BIND",
|
|
||||||
"com.google.android.gms.trustlet.nfc.service.BIND",
|
|
||||||
"com.google.android.gms.trustlet.onbody.service.BIND",
|
|
||||||
"com.google.android.gms.trustlet.place.service.BIND",
|
|
||||||
"com.google.android.gms.trustlet.voiceunlock.service.BIND",
|
|
||||||
"com.google.android.gms.udc.service.START",
|
|
||||||
"com.google.android.gms.update.START_API_SERVICE",
|
|
||||||
"com.google.android.gms.update.START_SERVICE",
|
|
||||||
"com.google.android.gms.update.START_SINGLE_USER_API_SERVICE",
|
|
||||||
"com.google.android.gms.update.START_TV_API_SERVICE",
|
|
||||||
"com.google.android.gms.usagereporting.service.START",
|
|
||||||
"com.google.android.gms.userlocation.service.START",
|
|
||||||
"com.google.android.gms.vehicle.cabin.service.START",
|
|
||||||
"com.google.android.gms.vehicle.climate.service.START",
|
|
||||||
"com.google.android.gms.vehicle.info.service.START",
|
|
||||||
"com.google.android.gms.wallet.service.BIND",
|
|
||||||
"com.google.android.gms.walletp2p.service.firstparty.BIND",
|
|
||||||
"com.google.android.gms.walletp2p.service.zeroparty.BIND",
|
|
||||||
"com.google.android.gms.wearable.BIND",
|
|
||||||
"com.google.android.gms.wearable.BIND_LISTENER",
|
|
||||||
"com.google.android.gms.wearable.DATA_CHANGED",
|
|
||||||
"com.google.android.gms.wearable.MESSAGE_RECEIVED",
|
|
||||||
"com.google.android.gms.wearable.NODE_CHANGED",
|
|
||||||
"com.google.android.gsf.action.GET_GLS",
|
|
||||||
"com.google.android.location.settings.LOCATION_REPORTING_SETTINGS",
|
|
||||||
"com.google.android.mdd.service.START",
|
|
||||||
"com.google.android.mdh.service.listener.START",
|
|
||||||
"com.google.android.mdh.service.START",
|
|
||||||
"com.google.android.mobstore.service.START",
|
|
||||||
"com.google.firebase.auth.api.gms.service.START",
|
|
||||||
"com.google.firebase.dynamiclinks.service.START",
|
|
||||||
"com.google.iid.TOKEN_REQUEST",
|
|
||||||
"com.google.android.gms.location.places.ui.PICK_PLACE",
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* All content provider authorities.
|
|
||||||
*/
|
|
||||||
val AUTHORITIES = setOf(
|
|
||||||
"com.google.android.gms.auth.accounts",
|
|
||||||
"com.google.android.gms.chimera",
|
|
||||||
"com.google.android.gms.fonts",
|
|
||||||
"com.google.android.gms.phenotype",
|
|
||||||
"com.google.android.gsf.gservices",
|
|
||||||
"com.google.settings",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract resource patch that allows Google apps to run without root and under a different package name
|
* Abstract resource patch that allows Google apps to run without root and under a different package name
|
||||||
* by using GmsCore instead of Google Play Services.
|
* by using GmsCore instead of Google Play Services.
|
||||||
@@ -510,129 +214,139 @@ private object Constants {
|
|||||||
* @param executeBlock The additional execution block of the patch.
|
* @param executeBlock The additional execution block of the patch.
|
||||||
* @param block The additional block to build the patch.
|
* @param block The additional block to build the patch.
|
||||||
*/
|
*/
|
||||||
fun gmsCoreSupportResourcePatch( // This is here only for binary compatibility.
|
fun gmsCoreSupportResourcePatch(
|
||||||
fromPackageName: String,
|
fromPackageName: String,
|
||||||
toPackageName: String,
|
toPackageName: String,
|
||||||
spoofedPackageSignature: String,
|
spoofedPackageSignature: String,
|
||||||
gmsCoreVendorGroupIdOption: Option<String>,
|
gmsCoreVendorGroupIdOption: Option<String>,
|
||||||
executeBlock: ResourcePatchContext.() -> Unit = {},
|
executeBlock: ResourcePatchContext.() -> Unit = {},
|
||||||
block: ResourcePatchBuilder.() -> Unit = {},
|
block: ResourcePatchBuilder.() -> Unit = {},
|
||||||
) = gmsCoreSupportResourcePatch(
|
|
||||||
fromPackageName,
|
|
||||||
toPackageName,
|
|
||||||
spoofedPackageSignature,
|
|
||||||
gmsCoreVendorGroupIdOption,
|
|
||||||
true,
|
|
||||||
executeBlock,
|
|
||||||
block
|
|
||||||
)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract resource patch that allows Google apps to run without root and under a different package name
|
|
||||||
* by using GmsCore instead of Google Play Services.
|
|
||||||
*
|
|
||||||
* @param fromPackageName The package name of the original app.
|
|
||||||
* @param toPackageName The package name to fall back to if no custom package name is specified in patch options.
|
|
||||||
* @param spoofedPackageSignature The signature of the package to spoof to.
|
|
||||||
* @param gmsCoreVendorGroupIdOption The option to get the vendor group ID of GmsCore.
|
|
||||||
* @param addStringResources If the GmsCore shared strings should be added to the patched app.
|
|
||||||
* @param executeBlock The additional execution block of the patch.
|
|
||||||
* @param block The additional block to build the patch.
|
|
||||||
*/
|
|
||||||
// TODO: On the next major release make this public and delete the public overloaded constructor.
|
|
||||||
internal fun gmsCoreSupportResourcePatch(
|
|
||||||
fromPackageName: String,
|
|
||||||
toPackageName: String,
|
|
||||||
spoofedPackageSignature: String,
|
|
||||||
gmsCoreVendorGroupIdOption: Option<String>,
|
|
||||||
addStringResources: Boolean = true,
|
|
||||||
executeBlock: ResourcePatchContext.() -> Unit = {},
|
|
||||||
block: ResourcePatchBuilder.() -> Unit = {},
|
|
||||||
) = resourcePatch {
|
) = resourcePatch {
|
||||||
dependsOn(
|
dependsOn(
|
||||||
changePackageNamePatch,
|
changePackageNamePatch,
|
||||||
addResourcesPatch,
|
addResourcesPatch,
|
||||||
)
|
)
|
||||||
|
|
||||||
val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
|
val gmsCoreVendorGroupId = gmsCoreVendorGroupIdOption.value!!
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
// Some patches don't use shared String resources so there's no need to add them.
|
addResources("shared", "misc.gms.gmsCoreSupportResourcePatch")
|
||||||
if (addStringResources) {
|
|
||||||
addResources("shared", "misc.gms.gmsCoreSupportResourcePatch")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
document("AndroidManifest.xml").use { document ->
|
||||||
* Add metadata to manifest to support spoofing the package name and signature of GmsCore.
|
document.getElementsByTagName("permission").asSequence().forEach { node ->
|
||||||
*/
|
val nameElement = node.attributes.getNamedItem("android:name")
|
||||||
fun addSpoofingMetadata() {
|
nameElement.textContent = toPackageName + nameElement.textContent
|
||||||
fun Node.adoptChild(
|
|
||||||
tagName: String,
|
|
||||||
block: Element.() -> Unit,
|
|
||||||
) {
|
|
||||||
val child = ownerDocument.createElement(tagName)
|
|
||||||
child.block()
|
|
||||||
appendChild(child)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
document("AndroidManifest.xml").use { document ->
|
document.getElementsByTagName("uses-permission").asSequence().forEach { node ->
|
||||||
val applicationNode =
|
val nameElement = node.attributes.getNamedItem("android:name")
|
||||||
document
|
if (nameElement.textContent in GMS_PERMISSIONS) {
|
||||||
.getElementsByTagName("application")
|
nameElement.textContent.replace("com.google", gmsCoreVendorGroupId)
|
||||||
.item(0)
|
|
||||||
|
|
||||||
// Spoof package name and signature.
|
|
||||||
applicationNode.adoptChild("meta-data") {
|
|
||||||
setAttribute("android:name", "$gmsCoreVendorGroupId.android.gms.SPOOFED_PACKAGE_NAME")
|
|
||||||
setAttribute("android:value", fromPackageName)
|
|
||||||
}
|
|
||||||
|
|
||||||
applicationNode.adoptChild("meta-data") {
|
|
||||||
setAttribute("android:name", "$gmsCoreVendorGroupId.android.gms.SPOOFED_PACKAGE_SIGNATURE")
|
|
||||||
setAttribute("android:value", spoofedPackageSignature)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GmsCore presence detection in extension.
|
|
||||||
applicationNode.adoptChild("meta-data") {
|
|
||||||
// TODO: The name of this metadata should be dynamic.
|
|
||||||
setAttribute("android:name", "app.revanced.MICROG_PACKAGE_NAME")
|
|
||||||
setAttribute("android:value", "$gmsCoreVendorGroupId.android.gms")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
document.getElementsByTagName("provider").asSequence().forEach { node ->
|
||||||
* Patch the manifest to support GmsCore.
|
val providerElement = node.attributes.getNamedItem("android:authorities")
|
||||||
*/
|
|
||||||
fun patchManifest() {
|
|
||||||
val packageName = setOrGetFallbackPackageName(toPackageName)
|
|
||||||
|
|
||||||
val transformations = mapOf(
|
providerElement.textContent = providerElement.textContent.split(";")
|
||||||
"package=\"$fromPackageName" to "package=\"$packageName",
|
.joinToString(";") { authority ->
|
||||||
"android:authorities=\"$fromPackageName" to "android:authorities=\"$packageName",
|
if (authority.startsWith("com.google")) {
|
||||||
"$fromPackageName.permission.C2D_MESSAGE" to "$packageName.permission.C2D_MESSAGE",
|
authority.replace("com.google", gmsCoreVendorGroupId)
|
||||||
"$fromPackageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION" to "$packageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
|
} else {
|
||||||
"com.google.android.c2dm" to "$gmsCoreVendorGroupId.android.c2dm",
|
"$gmsCoreVendorGroupId.$authority"
|
||||||
"com.google.android.libraries.photos.api.mars" to "$gmsCoreVendorGroupId.android.apps.photos.api.mars",
|
}
|
||||||
"</queries>" to "<package android:name=\"$gmsCoreVendorGroupId.android.gms\"/></queries>",
|
}
|
||||||
)
|
}
|
||||||
|
|
||||||
val manifest = get("AndroidManifest.xml")
|
document.getNode("manifest")
|
||||||
manifest.writeText(
|
.attributes.getNamedItem("package").textContent =
|
||||||
transformations.entries.fold(manifest.readText()) { acc, (from, to) ->
|
setOrGetFallbackPackageName(toPackageName)
|
||||||
acc.replace(
|
|
||||||
from,
|
document.getNode("queries").appendChild(
|
||||||
to,
|
document.createElement("package").apply {
|
||||||
|
attributes.setNamedItem(
|
||||||
|
document.createAttribute("android:name").apply {
|
||||||
|
textContent = "$gmsCoreVendorGroupId.android.gms"
|
||||||
|
},
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
patchManifest()
|
val applicationNode = document.getNode("application")
|
||||||
addSpoofingMetadata()
|
|
||||||
|
// Spoof package name and signature.
|
||||||
|
applicationNode.appendChild(
|
||||||
|
document.createElement("meta-data").apply {
|
||||||
|
setAttribute(
|
||||||
|
"android:name",
|
||||||
|
"$gmsCoreVendorGroupId.android.gms.SPOOFED_PACKAGE_NAME",
|
||||||
|
)
|
||||||
|
setAttribute("android:value", fromPackageName)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
applicationNode.appendChild(
|
||||||
|
document.createElement("meta-data").apply {
|
||||||
|
setAttribute(
|
||||||
|
"android:name",
|
||||||
|
"$gmsCoreVendorGroupId.android.gms.SPOOFED_PACKAGE_SIGNATURE",
|
||||||
|
)
|
||||||
|
setAttribute("android:value", spoofedPackageSignature)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
// GmsCore presence detection in extension.
|
||||||
|
applicationNode.appendChild(
|
||||||
|
document.createElement("meta-data").apply {
|
||||||
|
// TODO: The name of this metadata should be dynamic.
|
||||||
|
setAttribute("android:name", "app.revanced.MICROG_PACKAGE_NAME")
|
||||||
|
setAttribute("android:value", "$gmsCoreVendorGroupId.android.gms")
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
executeBlock()
|
executeBlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
block()
|
block()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private object Constants {
|
||||||
|
val GMS_PERMISSIONS = setOf(
|
||||||
|
"com.google.android.providers.gsf.permission.READ_GSERVICES",
|
||||||
|
"com.google.android.c2dm.permission.RECEIVE",
|
||||||
|
"com.google.android.c2dm.permission.SEND",
|
||||||
|
"com.google.android.gtalkservice.permission.GTALK_SERVICE",
|
||||||
|
"com.google.android.googleapps.permission.GOOGLE_AUTH",
|
||||||
|
"com.google.android.googleapps.permission.GOOGLE_AUTH.cp",
|
||||||
|
"com.google.android.googleapps.permission.GOOGLE_AUTH.local",
|
||||||
|
"com.google.android.googleapps.permission.GOOGLE_AUTH.mail",
|
||||||
|
"com.google.android.googleapps.permission.GOOGLE_AUTH.writely",
|
||||||
|
"com.google.android.gms.permission.ACTIVITY_RECOGNITION",
|
||||||
|
"com.google.android.gms.permission.AD_ID",
|
||||||
|
"com.google.android.gms.permission.AD_ID_NOTIFICATION",
|
||||||
|
"com.google.android.gms.auth.api.phone.permission.SEND",
|
||||||
|
"com.google.android.gms.permission.CAR_INFORMATION",
|
||||||
|
"com.google.android.gms.permission.CAR_SPEED",
|
||||||
|
"com.google.android.gms.permission.CAR_FUEL",
|
||||||
|
"com.google.android.gms.permission.CAR_MILEAGE",
|
||||||
|
"com.google.android.gms.permission.CAR_VENDOR_EXTENSION",
|
||||||
|
"com.google.android.gms.locationsharingreporter.periodic.STATUS_UPDATE",
|
||||||
|
"com.google.android.gms.auth.permission.GOOGLE_ACCOUNT_CHANGE",
|
||||||
|
)
|
||||||
|
|
||||||
|
val GMS_AUTHORITIES = setOf(
|
||||||
|
"google.android.gms.fileprovider",
|
||||||
|
"com.google.android.gms.auth.accounts",
|
||||||
|
"com.google.android.gms.chimera",
|
||||||
|
"com.google.android.gms.fonts",
|
||||||
|
"com.google.android.gms.phenotype",
|
||||||
|
"com.google.android.gsf.gservices",
|
||||||
|
"com.google.settings",
|
||||||
|
"subscribedfeeds",
|
||||||
|
)
|
||||||
|
|
||||||
|
val APP_PERMISSIONS = mutableSetOf<String>()
|
||||||
|
|
||||||
|
val APP_AUTHORITIES = mutableSetOf<String>()
|
||||||
|
}
|
||||||
|
|||||||
+17
-9
@@ -6,6 +6,8 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch
|
|||||||
import app.revanced.patches.shared.castContextFetchFingerprint
|
import app.revanced.patches.shared.castContextFetchFingerprint
|
||||||
import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch
|
import app.revanced.patches.shared.misc.gms.gmsCoreSupportPatch
|
||||||
import app.revanced.patches.shared.misc.settings.preference.IntentPreference
|
import app.revanced.patches.shared.misc.settings.preference.IntentPreference
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference
|
||||||
|
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||||
import app.revanced.patches.shared.primeMethodFingerprint
|
import app.revanced.patches.shared.primeMethodFingerprint
|
||||||
import app.revanced.patches.youtube.layout.buttons.overlay.hidePlayerOverlayButtonsPatch
|
import app.revanced.patches.youtube.layout.buttons.overlay.hidePlayerOverlayButtonsPatch
|
||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||||
@@ -39,7 +41,7 @@ val gmsCoreSupportPatch = gmsCoreSupportPatch(
|
|||||||
"20.07.39",
|
"20.07.39",
|
||||||
"20.13.41",
|
"20.13.41",
|
||||||
"20.14.43",
|
"20.14.43",
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,18 +58,24 @@ private fun gmsCoreSupportResourcePatch(
|
|||||||
val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
|
val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
|
||||||
|
|
||||||
PreferenceScreen.MISC.addPreferences(
|
PreferenceScreen.MISC.addPreferences(
|
||||||
IntentPreference(
|
PreferenceScreenPreference(
|
||||||
"microg_settings",
|
"revanced_gms_core_screen",
|
||||||
intent = IntentPreference.Intent("", "org.microg.gms.ui.SettingsActivity") {
|
preferences = setOf(
|
||||||
"$gmsCoreVendorGroupId.android.gms"
|
SwitchPreference("revanced_gms_core_check_updates"),
|
||||||
}
|
IntentPreference(
|
||||||
)
|
"revanced_gms_core_settings",
|
||||||
|
intent = IntentPreference.Intent("", "org.microg.gms.ui.SettingsActivity") {
|
||||||
|
"$gmsCoreVendorGroupId.android.gms"
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
) {
|
) {
|
||||||
dependsOn(
|
dependsOn(
|
||||||
addResourcesPatch,
|
addResourcesPatch,
|
||||||
settingsPatch,
|
settingsPatch,
|
||||||
accountCredentialsInvalidTextPatch
|
accountCredentialsInvalidTextPatch,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,23 +101,31 @@ To translate new languages or improve the existing translations, visit translate
|
|||||||
and changes made here must also be made there. -->
|
and changes made here must also be made there. -->
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.gms.gmsCoreSupportResourcePatch">
|
<patch id="misc.gms.gmsCoreSupportResourcePatch">
|
||||||
<string name="microg_settings_title">GmsCore Settings</string>
|
<string name="revanced_gms_core_screen_title">GmsCore</string>
|
||||||
<string name="microg_settings_summary">Settings for GmsCore</string>
|
<string name="revanced_gms_core_screen_summary">Settings related to GmsCore</string>
|
||||||
|
<string name="revanced_gms_core_check_updates_title">Check for GmsCore updates</string>
|
||||||
|
<string name="revanced_gms_core_check_updates_summary_on">Checking for updates is enabled</string>
|
||||||
|
<string name="revanced_gms_core_check_updates_summary_off">Checking for updates is disabled</string>
|
||||||
|
<string name="revanced_gms_core_settings_title">Open GmsCore Settings</string>
|
||||||
|
<string name="revanced_gms_core_settings_summary">Settings of GmsCore</string>
|
||||||
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
<!-- Translations of this should not be longer than the original English text, otherwise the text can be clipped and not entirely shown. -->
|
||||||
<string name="gms_core_toast_not_installed_message">MicroG GmsCore is not installed. Install it.</string>
|
<string name="revanced_gms_core_toast_not_installed_message">MicroG GmsCore is not installed. Install it.</string>
|
||||||
<string name="gms_core_dialog_title">Action needed</string>
|
<string name="revanced_gms_core_dialog_title">Action needed</string>
|
||||||
<string name="gms_core_dialog_not_whitelisted_not_allowed_in_background_message">"MicroG GmsCore does not have permission to run in the background.
|
<string name="revanced_gms_core_toast_update_check_failed_message">Failed to check for MicroG GmsCore updates</string>
|
||||||
|
<string name="revanced_gms_core_update_available_message">A new version (%s) of MicroG GmsCore is available. Currently, you are using version %s.</string>
|
||||||
|
<string name="revanced_gms_core_dialog_not_whitelisted_not_allowed_in_background_message">"MicroG GmsCore does not have permission to run in the background.
|
||||||
|
|
||||||
Follow the \"Don\'t kill my app\" guide for your phone, and apply the instructions to your MicroG installation.
|
Follow the \"Don\'t kill my app\" guide for your phone, and apply the instructions to your MicroG installation.
|
||||||
|
|
||||||
This is required for the app to work."</string>
|
This is required for the app to work."</string>
|
||||||
<string name="gms_core_dialog_open_website_text">Open website</string>
|
<string name="revanced_gms_core_dialog_open_website_text">Open website</string>
|
||||||
<string name="gms_core_dialog_not_whitelisted_using_battery_optimizations_message">"MicroG GmsCore battery optimizations must be disabled to prevent issues.
|
<string name="revanced_gms_core_dialog_cancel_text">Cancel</string>
|
||||||
|
<string name="revanced_gms_core_dialog_not_whitelisted_using_battery_optimizations_message">"MicroG GmsCore battery optimizations must be disabled to prevent issues.
|
||||||
|
|
||||||
Disabling battery optimizations for MicroG will not negatively affect battery usage.
|
Disabling battery optimizations for MicroG will not negatively affect battery usage.
|
||||||
|
|
||||||
Tap the continue button and allow optimization changes."</string>
|
Tap the continue button and allow optimization changes."</string>
|
||||||
<string name="gms_core_dialog_continue_text">Continue</string>
|
<string name="revanced_gms_core_dialog_continue_text">Continue</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
|
<patch id="misc.fix.playback.spoofVideoStreamsPatch">
|
||||||
<string name="revanced_spoof_video_streams_screen_title">Spoof video streams</string>
|
<string name="revanced_spoof_video_streams_screen_title">Spoof video streams</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user