feat(GmsCore support): Reduce amount of necessary changes and add update check (#6582)
This commit is contained in:
parent
6f70167369
commit
650e6a2710
9 changed files with 571 additions and 696 deletions
|
|
@ -1,8 +1,5 @@
|
|||
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.app.Activity;
|
||||
import android.app.Dialog;
|
||||
|
|
@ -17,46 +14,53 @@ import android.os.PowerManager;
|
|||
import android.provider.Settings;
|
||||
import android.util.Pair;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
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.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.Locale;
|
||||
|
||||
import app.revanced.extension.shared.requests.Requester;
|
||||
import app.revanced.extension.shared.requests.Route;
|
||||
import app.revanced.extension.shared.ui.CustomDialog;
|
||||
import static app.revanced.extension.shared.StringRef.str;
|
||||
import static app.revanced.extension.shared.requests.Route.Method.GET;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class GmsCoreSupport {
|
||||
private static final String GMS_CORE_PACKAGE_NAME
|
||||
= 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(" ", "-");
|
||||
private static GmsCore gmsCore = GmsCore.UNKNOWN;
|
||||
|
||||
/**
|
||||
* If a manufacturer specific page exists on DontKillMyApp.
|
||||
*/
|
||||
@Nullable
|
||||
private static volatile Boolean DONT_KILL_MY_APP_MANUFACTURER_SUPPORTED;
|
||||
|
||||
private static String getOriginalPackageName() {
|
||||
return null; // Modified during patching.
|
||||
static {
|
||||
for (GmsCore core : GmsCore.values()) {
|
||||
if (core.getGroupId().equals(getGmsCoreVendorGroupId())) {
|
||||
GmsCoreSupport.gmsCore = core;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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() {
|
||||
String originalPackageName = getOriginalPackageName();
|
||||
|
|
@ -64,203 +68,336 @@ public class GmsCoreSupport {
|
|||
|| originalPackageName.equals(Utils.getContext().getPackageName());
|
||||
}
|
||||
|
||||
private static 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 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.
|
||||
private enum GmsCore {
|
||||
REVANCED("app.revanced", "https://github.com/revanced/gmscore/releases/latest", () -> {
|
||||
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(
|
||||
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.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");
|
||||
DONT_KILL_MY_APP_MANUFACTURER_SUPPORTED = supported;
|
||||
int responseCode = connection.getResponseCode();
|
||||
if (responseCode != 200) {
|
||||
Logger.printDebug(() -> "GitHub API returned status code: " + responseCode);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
Logger.printInfo(() -> "Could not check if manufacturer is listed on DontKillMyApp: "
|
||||
+ BUILD_MANUFACTURER, ex);
|
||||
DONT_KILL_MY_APP_MANUFACTURER_SUPPORTED = null;
|
||||
Logger.printInfo(() -> "Failed to fetch latest GmsCore version from GitHub", ex);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}),
|
||||
UNKNOWN(getGmsCoreVendorGroupId(), getGmsCoreVendorGroupId() + "android.gms", () -> null);
|
||||
|
||||
private static void openDontKillMyApp() {
|
||||
final Boolean manufacturerSupported = DONT_KILL_MY_APP_MANUFACTURER_SUPPORTED;
|
||||
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(" ", "-");
|
||||
|
||||
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";
|
||||
/**
|
||||
* If a manufacturer specific page exists on DontKillMyApp.
|
||||
*/
|
||||
@Nullable
|
||||
private volatile Boolean dontKillMyAppManufacturerSupported;
|
||||
|
||||
private final String groupId;
|
||||
private final String packageName;
|
||||
private final String downloadQuery;
|
||||
private final GetLatestVersion getLatestVersion;
|
||||
private final Uri gmsCoreProvider;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If GmsCore is not whitelisted from battery optimizations.
|
||||
*/
|
||||
private static 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;
|
||||
String getGroupId() {
|
||||
return groupId;
|
||||
}
|
||||
|
||||
void check(Activity context) {
|
||||
checkInstallation(context);
|
||||
checkUpdates(context);
|
||||
}
|
||||
|
||||
private void checkInstallation(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 {
|
||||
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) {
|
||||
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
|
||||
}
|
||||
|
||||
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.
|
||||
@FunctionalInterface
|
||||
private interface GetLatestVersion {
|
||||
String get();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 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.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -27,5 +27,4 @@ private fun gmsCoreSupportResourcePatch(
|
|||
toPackageName = REVANCED_MAGAZINES_PACKAGE_NAME,
|
||||
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a666",
|
||||
gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
|
||||
addStringResources = false,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ private fun gmsCoreSupportResourcePatch(
|
|||
) = app.revanced.patches.shared.misc.gms.gmsCoreSupportResourcePatch(
|
||||
fromPackageName = PHOTOS_PACKAGE_NAME,
|
||||
toPackageName = REVANCED_PHOTOS_PACKAGE_NAME,
|
||||
addStringResources = false,
|
||||
spoofedPackageSignature = "24bb24c05e47e0aefa68a58a766179d9b613a600",
|
||||
gmsCoreVendorGroupIdOption = gmsCoreVendorGroupIdOption,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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.addResourcesPatch
|
||||
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.REVANCED_MUSIC_PACKAGE_NAME
|
||||
import app.revanced.patches.music.misc.settings.PreferenceScreen
|
||||
import app.revanced.patches.music.misc.settings.settingsPatch
|
||||
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.misc.gms.gmsCoreSupportPatch
|
||||
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
|
||||
|
||||
@Suppress("unused")
|
||||
|
|
@ -32,8 +34,8 @@ val gmsCoreSupportPatch = gmsCoreSupportPatch(
|
|||
compatibleWith(
|
||||
MUSIC_PACKAGE_NAME(
|
||||
"7.29.52",
|
||||
"8.10.52"
|
||||
)
|
||||
"8.10.52",
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -50,21 +52,27 @@ private fun gmsCoreSupportResourcePatch(
|
|||
val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
|
||||
|
||||
PreferenceScreen.MISC.addPreferences(
|
||||
IntentPreference(
|
||||
"microg_settings",
|
||||
intent = IntentPreference.Intent("", "org.microg.gms.ui.SettingsActivity") {
|
||||
"$gmsCoreVendorGroupId.android.gms"
|
||||
}
|
||||
)
|
||||
PreferenceScreenPreference(
|
||||
"revanced_gms_core_screen",
|
||||
preferences = setOf(
|
||||
SwitchPreference("revanced_gms_core_check_updates"),
|
||||
IntentPreference(
|
||||
"revanced_gms_core_settings",
|
||||
intent = IntentPreference.Intent("", "org.microg.gms.ui.SettingsActivity") {
|
||||
"$gmsCoreVendorGroupId.android.gms"
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
) {
|
||||
dependsOn(
|
||||
addResourcesPatch,
|
||||
settingsPatch,
|
||||
fileProviderPatch(
|
||||
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")
|
||||
}
|
||||
|
||||
internal val gmsCoreSupportFingerprint = fingerprint {
|
||||
internal val getGmsCoreVendorGroupIdFingerprint = fingerprint {
|
||||
accessFlags(AccessFlags.PRIVATE, AccessFlags.STATIC)
|
||||
returns("Ljava/lang/String;")
|
||||
parameters()
|
||||
|
|
|
|||
|
|
@ -2,26 +2,18 @@ package app.revanced.patches.shared.misc.gms
|
|||
|
||||
import app.revanced.patcher.Fingerprint
|
||||
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.patch.BytecodePatchBuilder
|
||||
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.patcher.patch.*
|
||||
import app.revanced.patches.all.misc.packagename.changePackageNamePatch
|
||||
import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName
|
||||
import app.revanced.patches.all.misc.resources.addResources
|
||||
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.AUTHORITIES
|
||||
import app.revanced.patches.shared.misc.gms.Constants.PERMISSIONS
|
||||
import app.revanced.util.getReference
|
||||
import app.revanced.util.returnEarly
|
||||
import app.revanced.patches.shared.misc.gms.Constants.APP_AUTHORITIES
|
||||
import app.revanced.patches.shared.misc.gms.Constants.APP_PERMISSIONS
|
||||
import app.revanced.patches.shared.misc.gms.Constants.GMS_AUTHORITIES
|
||||
import app.revanced.patches.shared.misc.gms.Constants.GMS_PERMISSIONS
|
||||
import app.revanced.util.*
|
||||
import com.android.tools.smali.dexlib2.Opcode
|
||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c
|
||||
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.immutable.reference.ImmutableStringReference
|
||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||
import org.w3c.dom.Element
|
||||
import org.w3c.dom.Node
|
||||
import java.net.URI
|
||||
|
||||
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/GmsCoreSupport;"
|
||||
|
||||
|
|
@ -69,10 +60,7 @@ fun gmsCoreSupportPatch(
|
|||
val gmsCoreVendorGroupIdOption = stringOption(
|
||||
key = "gmsCoreVendorGroupId",
|
||||
default = "app.revanced",
|
||||
values =
|
||||
mapOf(
|
||||
"ReVanced" to "app.revanced",
|
||||
),
|
||||
values = mapOf("ReVanced" to "app.revanced"),
|
||||
title = "GmsCore vendor group ID",
|
||||
description = "The vendor's group ID for GmsCore.",
|
||||
required = true,
|
||||
|
|
@ -84,9 +72,9 @@ fun gmsCoreSupportPatch(
|
|||
extensionPatch,
|
||||
)
|
||||
|
||||
val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
|
||||
|
||||
execute {
|
||||
val gmsCoreVendorGroupId = gmsCoreVendorGroupIdOption.value!!
|
||||
|
||||
fun transformStringReferences(transform: (str: String) -> String?) = classes.forEach {
|
||||
val mutableClass by lazy {
|
||||
proxy(it).mutableClass
|
||||
|
|
@ -118,80 +106,54 @@ fun gmsCoreSupportPatch(
|
|||
}
|
||||
}
|
||||
|
||||
// region Collection of transformations that are applied to all strings.
|
||||
|
||||
fun commonTransform(referencedString: String): String? = when (referencedString) {
|
||||
fun transformPackages(string: String): String? = when (string) {
|
||||
"com.google",
|
||||
"com.google.android.gms",
|
||||
in PERMISSIONS,
|
||||
in ACTIONS,
|
||||
in AUTHORITIES,
|
||||
-> referencedString.replace("com.google", gmsCoreVendorGroupId!!)
|
||||
in GMS_PERMISSIONS,
|
||||
in GMS_AUTHORITIES,
|
||||
-> if (string.startsWith("com.google")) {
|
||||
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
|
||||
}
|
||||
|
||||
fun contentUrisTransform(str: String): String? {
|
||||
// only when content:// uri
|
||||
if (str.startsWith("content://")) {
|
||||
// check if matches any authority
|
||||
for (authority in AUTHORITIES) {
|
||||
val uriPrefix = "content://$authority"
|
||||
if (str.startsWith(uriPrefix)) {
|
||||
return str.replace(
|
||||
uriPrefix,
|
||||
"content://${authority.replace("com.google", gmsCoreVendorGroupId!!)}",
|
||||
)
|
||||
}
|
||||
fun transformContentUrlAuthority(string: String) = if (!string.startsWith("content://")) {
|
||||
null
|
||||
} else {
|
||||
runCatching { URI.create(string) }.map {
|
||||
when (it.authority) {
|
||||
in GMS_AUTHORITIES ->
|
||||
if (it.authority.startsWith("com.google")) {
|
||||
string.replace("com.google", gmsCoreVendorGroupId)
|
||||
} else {
|
||||
string.replace(
|
||||
it.authority,
|
||||
"$gmsCoreVendorGroupId.${it.authority}",
|
||||
)
|
||||
}
|
||||
|
||||
in APP_AUTHORITIES ->
|
||||
string.replace(it.authority, "$toPackageName.${it.authority}")
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
// 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
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
// Transform all strings using all provided transforms, first match wins.
|
||||
val transformations = arrayOf(
|
||||
::commonTransform,
|
||||
::contentUrisTransform,
|
||||
packageNameTransform(fromPackageName, packageName),
|
||||
::transformPackages,
|
||||
::transformContentUrlAuthority,
|
||||
)
|
||||
|
||||
transformStringReferences transform@{ string ->
|
||||
transformations.forEach { transform ->
|
||||
transform(string)?.let { transformedString -> return@transform transformedString }
|
||||
|
|
@ -201,16 +163,26 @@ fun gmsCoreSupportPatch(
|
|||
}
|
||||
|
||||
// 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.
|
||||
earlyReturnFingerprints.forEach { it.method.returnEarly() }
|
||||
serviceCheckFingerprint.method.returnEarly()
|
||||
|
||||
// Google Play Utility is not present in all apps, so we need to check if it's present.
|
||||
if (googlePlayUtilityFingerprint.methodOrNull != null) {
|
||||
googlePlayUtilityFingerprint.method.returnEarly(0)
|
||||
}
|
||||
googlePlayUtilityFingerprint.methodOrNull?.returnEarly(0)
|
||||
|
||||
// Set original and patched package names for extension to use.
|
||||
originalPackageNameExtensionFingerprint.method.returnEarly(fromPackageName)
|
||||
|
|
@ -219,11 +191,11 @@ fun gmsCoreSupportPatch(
|
|||
mainActivityOnCreateFingerprint.method.addInstruction(
|
||||
0,
|
||||
"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.
|
||||
gmsCoreSupportFingerprint.method.returnEarly(gmsCoreVendorGroupId!!)
|
||||
getGmsCoreVendorGroupIdFingerprint.method.returnEarly(gmsCoreVendorGroupId)
|
||||
|
||||
executeBlock()
|
||||
}
|
||||
|
|
@ -231,274 +203,6 @@ fun gmsCoreSupportPatch(
|
|||
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
|
||||
* 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 block The additional block to build the patch.
|
||||
*/
|
||||
fun gmsCoreSupportResourcePatch( // This is here only for binary compatibility.
|
||||
fun gmsCoreSupportResourcePatch(
|
||||
fromPackageName: String,
|
||||
toPackageName: String,
|
||||
spoofedPackageSignature: String,
|
||||
gmsCoreVendorGroupIdOption: Option<String>,
|
||||
executeBlock: ResourcePatchContext.() -> 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 {
|
||||
dependsOn(
|
||||
changePackageNamePatch,
|
||||
addResourcesPatch,
|
||||
)
|
||||
|
||||
val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
|
||||
val gmsCoreVendorGroupId = gmsCoreVendorGroupIdOption.value!!
|
||||
|
||||
execute {
|
||||
// Some patches don't use shared String resources so there's no need to add them.
|
||||
if (addStringResources) {
|
||||
addResources("shared", "misc.gms.gmsCoreSupportResourcePatch")
|
||||
}
|
||||
addResources("shared", "misc.gms.gmsCoreSupportResourcePatch")
|
||||
|
||||
/**
|
||||
* Add metadata to manifest to support spoofing the package name and signature of GmsCore.
|
||||
*/
|
||||
fun addSpoofingMetadata() {
|
||||
fun Node.adoptChild(
|
||||
tagName: String,
|
||||
block: Element.() -> Unit,
|
||||
) {
|
||||
val child = ownerDocument.createElement(tagName)
|
||||
child.block()
|
||||
appendChild(child)
|
||||
document("AndroidManifest.xml").use { document ->
|
||||
document.getElementsByTagName("permission").asSequence().forEach { node ->
|
||||
val nameElement = node.attributes.getNamedItem("android:name")
|
||||
nameElement.textContent = toPackageName + nameElement.textContent
|
||||
}
|
||||
|
||||
document("AndroidManifest.xml").use { document ->
|
||||
val applicationNode =
|
||||
document
|
||||
.getElementsByTagName("application")
|
||||
.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("uses-permission").asSequence().forEach { node ->
|
||||
val nameElement = node.attributes.getNamedItem("android:name")
|
||||
if (nameElement.textContent in GMS_PERMISSIONS) {
|
||||
nameElement.textContent.replace("com.google", gmsCoreVendorGroupId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch the manifest to support GmsCore.
|
||||
*/
|
||||
fun patchManifest() {
|
||||
val packageName = setOrGetFallbackPackageName(toPackageName)
|
||||
document.getElementsByTagName("provider").asSequence().forEach { node ->
|
||||
val providerElement = node.attributes.getNamedItem("android:authorities")
|
||||
|
||||
val transformations = mapOf(
|
||||
"package=\"$fromPackageName" to "package=\"$packageName",
|
||||
"android:authorities=\"$fromPackageName" to "android:authorities=\"$packageName",
|
||||
"$fromPackageName.permission.C2D_MESSAGE" to "$packageName.permission.C2D_MESSAGE",
|
||||
"$fromPackageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION" to "$packageName.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
|
||||
"com.google.android.c2dm" to "$gmsCoreVendorGroupId.android.c2dm",
|
||||
"com.google.android.libraries.photos.api.mars" to "$gmsCoreVendorGroupId.android.apps.photos.api.mars",
|
||||
"</queries>" to "<package android:name=\"$gmsCoreVendorGroupId.android.gms\"/></queries>",
|
||||
)
|
||||
providerElement.textContent = providerElement.textContent.split(";")
|
||||
.joinToString(";") { authority ->
|
||||
if (authority.startsWith("com.google")) {
|
||||
authority.replace("com.google", gmsCoreVendorGroupId)
|
||||
} else {
|
||||
"$gmsCoreVendorGroupId.$authority"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val manifest = get("AndroidManifest.xml")
|
||||
manifest.writeText(
|
||||
transformations.entries.fold(manifest.readText()) { acc, (from, to) ->
|
||||
acc.replace(
|
||||
from,
|
||||
to,
|
||||
document.getNode("manifest")
|
||||
.attributes.getNamedItem("package").textContent =
|
||||
setOrGetFallbackPackageName(toPackageName)
|
||||
|
||||
document.getNode("queries").appendChild(
|
||||
document.createElement("package").apply {
|
||||
attributes.setNamedItem(
|
||||
document.createAttribute("android:name").apply {
|
||||
textContent = "$gmsCoreVendorGroupId.android.gms"
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
patchManifest()
|
||||
addSpoofingMetadata()
|
||||
val applicationNode = document.getNode("application")
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
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>()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import app.revanced.patches.all.misc.resources.addResourcesPatch
|
|||
import app.revanced.patches.shared.castContextFetchFingerprint
|
||||
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.PreferenceScreenPreference
|
||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
||||
import app.revanced.patches.shared.primeMethodFingerprint
|
||||
import app.revanced.patches.youtube.layout.buttons.overlay.hidePlayerOverlayButtonsPatch
|
||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
||||
|
|
@ -39,7 +41,7 @@ val gmsCoreSupportPatch = gmsCoreSupportPatch(
|
|||
"20.07.39",
|
||||
"20.13.41",
|
||||
"20.14.43",
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -56,18 +58,24 @@ private fun gmsCoreSupportResourcePatch(
|
|||
val gmsCoreVendorGroupId by gmsCoreVendorGroupIdOption
|
||||
|
||||
PreferenceScreen.MISC.addPreferences(
|
||||
IntentPreference(
|
||||
"microg_settings",
|
||||
intent = IntentPreference.Intent("", "org.microg.gms.ui.SettingsActivity") {
|
||||
"$gmsCoreVendorGroupId.android.gms"
|
||||
}
|
||||
)
|
||||
PreferenceScreenPreference(
|
||||
"revanced_gms_core_screen",
|
||||
preferences = setOf(
|
||||
SwitchPreference("revanced_gms_core_check_updates"),
|
||||
IntentPreference(
|
||||
"revanced_gms_core_settings",
|
||||
intent = IntentPreference.Intent("", "org.microg.gms.ui.SettingsActivity") {
|
||||
"$gmsCoreVendorGroupId.android.gms"
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
) {
|
||||
dependsOn(
|
||||
addResourcesPatch,
|
||||
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. -->
|
||||
</patch>
|
||||
<patch id="misc.gms.gmsCoreSupportResourcePatch">
|
||||
<string name="microg_settings_title">GmsCore Settings</string>
|
||||
<string name="microg_settings_summary">Settings for GmsCore</string>
|
||||
<string name="revanced_gms_core_screen_title">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. -->
|
||||
<string name="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="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_not_installed_message">MicroG GmsCore is not installed. Install it.</string>
|
||||
<string name="revanced_gms_core_dialog_title">Action needed</string>
|
||||
<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.
|
||||
|
||||
This is required for the app to work."</string>
|
||||
<string name="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_open_website_text">Open website</string>
|
||||
<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.
|
||||
|
||||
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 id="misc.fix.playback.spoofVideoStreamsPatch">
|
||||
<string name="revanced_spoof_video_streams_screen_title">Spoof video streams</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue