From 6312fe8d60da24465c0c1b0fa4e94ceb79873d9c Mon Sep 17 00:00:00 2001 From: 1fexd <58902674+1fexd@users.noreply.github.com> Date: Thu, 8 Jan 2026 00:06:05 +0000 Subject: [PATCH] feat: Disable Play Integrity patch (#6412) Co-authored-by: oSumAtrIX --- .../disable-play-integrity/build.gradle.kts | 20 ++++++ .../src/main/AndroidManifest.xml | 1 + .../protocol/IExpressIntegrityService.aidl | 8 +++ .../IExpressIntegrityServiceCallback.aidl | 5 ++ .../integrity/protocol/IIntegrityService.aidl | 8 +++ .../protocol/IIntegrityServiceCallback.aidl | 7 +++ .../src/main/java/android/ext/PackageId.java | 10 +++ .../main/java/android/os/BinderWrapper.java | 62 +++++++++++++++++++ .../ClassicPlayIntegrityServiceWrapper.java | 41 ++++++++++++ .../PlayIntegrityServiceWrapper.java | 48 ++++++++++++++ .../lib/playintegrity/PlayIntegrityUtils.java | 35 +++++++++++ .../StandardPlayIntegrityServiceWrapper.java | 42 +++++++++++++ .../lib/util/ServiceConnectionWrapper.java | 49 +++++++++++++++ .../DisablePlayIntegrityPatch.java | 17 +++++ .../internal/os/FakeBackgroundHandler.java | 11 ++++ patches/api/patches.api | 4 ++ .../playintegrity/DisablePlayIntegrity.kt | 55 ++++++++++++++++ 17 files changed, 423 insertions(+) create mode 100644 extensions/all/misc/disable-play-integrity/build.gradle.kts create mode 100644 extensions/all/misc/disable-play-integrity/src/main/AndroidManifest.xml create mode 100644 extensions/all/misc/disable-play-integrity/src/main/aidl/com/google/android/play/core/integrity/protocol/IExpressIntegrityService.aidl create mode 100644 extensions/all/misc/disable-play-integrity/src/main/aidl/com/google/android/play/core/integrity/protocol/IExpressIntegrityServiceCallback.aidl create mode 100644 extensions/all/misc/disable-play-integrity/src/main/aidl/com/google/android/play/core/integrity/protocol/IIntegrityService.aidl create mode 100644 extensions/all/misc/disable-play-integrity/src/main/aidl/com/google/android/play/core/integrity/protocol/IIntegrityServiceCallback.aidl create mode 100644 extensions/all/misc/disable-play-integrity/src/main/java/android/ext/PackageId.java create mode 100644 extensions/all/misc/disable-play-integrity/src/main/java/android/os/BinderWrapper.java create mode 100644 extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/playintegrity/ClassicPlayIntegrityServiceWrapper.java create mode 100644 extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/playintegrity/PlayIntegrityServiceWrapper.java create mode 100644 extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/playintegrity/PlayIntegrityUtils.java create mode 100644 extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/playintegrity/StandardPlayIntegrityServiceWrapper.java create mode 100644 extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/util/ServiceConnectionWrapper.java create mode 100644 extensions/all/misc/disable-play-integrity/src/main/java/app/revanced/extension/playintegrity/DisablePlayIntegrityPatch.java create mode 100644 extensions/all/misc/disable-play-integrity/src/main/java/com/android/internal/os/FakeBackgroundHandler.java create mode 100644 patches/src/main/kotlin/app/revanced/patches/all/misc/playintegrity/DisablePlayIntegrity.kt diff --git a/extensions/all/misc/disable-play-integrity/build.gradle.kts b/extensions/all/misc/disable-play-integrity/build.gradle.kts new file mode 100644 index 000000000..549297227 --- /dev/null +++ b/extensions/all/misc/disable-play-integrity/build.gradle.kts @@ -0,0 +1,20 @@ +android { + namespace = "app.revanced.extension" + + defaultConfig { + minSdk = 21 + } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + buildFeatures { + aidl = true + } +} + +dependencies { + compileOnly(libs.annotation) +} diff --git a/extensions/all/misc/disable-play-integrity/src/main/AndroidManifest.xml b/extensions/all/misc/disable-play-integrity/src/main/AndroidManifest.xml new file mode 100644 index 000000000..9b65eb06c --- /dev/null +++ b/extensions/all/misc/disable-play-integrity/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/extensions/all/misc/disable-play-integrity/src/main/aidl/com/google/android/play/core/integrity/protocol/IExpressIntegrityService.aidl b/extensions/all/misc/disable-play-integrity/src/main/aidl/com/google/android/play/core/integrity/protocol/IExpressIntegrityService.aidl new file mode 100644 index 000000000..7b8f59f1d --- /dev/null +++ b/extensions/all/misc/disable-play-integrity/src/main/aidl/com/google/android/play/core/integrity/protocol/IExpressIntegrityService.aidl @@ -0,0 +1,8 @@ +package com.google.android.play.core.integrity.protocol; + +import android.os.Bundle; +import com.google.android.play.core.integrity.protocol.IExpressIntegrityServiceCallback; + +interface IExpressIntegrityService { + oneway void requestIntegrityToken(in Bundle request, IExpressIntegrityServiceCallback callback) = 2; +} diff --git a/extensions/all/misc/disable-play-integrity/src/main/aidl/com/google/android/play/core/integrity/protocol/IExpressIntegrityServiceCallback.aidl b/extensions/all/misc/disable-play-integrity/src/main/aidl/com/google/android/play/core/integrity/protocol/IExpressIntegrityServiceCallback.aidl new file mode 100644 index 000000000..624167afb --- /dev/null +++ b/extensions/all/misc/disable-play-integrity/src/main/aidl/com/google/android/play/core/integrity/protocol/IExpressIntegrityServiceCallback.aidl @@ -0,0 +1,5 @@ +package com.google.android.play.core.integrity.protocol; + +interface IExpressIntegrityServiceCallback { + oneway void onRequestExpressIntegrityTokenResult(in Bundle result) = 2; +} diff --git a/extensions/all/misc/disable-play-integrity/src/main/aidl/com/google/android/play/core/integrity/protocol/IIntegrityService.aidl b/extensions/all/misc/disable-play-integrity/src/main/aidl/com/google/android/play/core/integrity/protocol/IIntegrityService.aidl new file mode 100644 index 000000000..bb1bcd551 --- /dev/null +++ b/extensions/all/misc/disable-play-integrity/src/main/aidl/com/google/android/play/core/integrity/protocol/IIntegrityService.aidl @@ -0,0 +1,8 @@ +package com.google.android.play.core.integrity.protocol; + +import android.os.Bundle; +import com.google.android.play.core.integrity.protocol.IIntegrityServiceCallback; + +interface IIntegrityService { + oneway void requestIntegrityToken(in Bundle request, IIntegrityServiceCallback callback) = 1; +} diff --git a/extensions/all/misc/disable-play-integrity/src/main/aidl/com/google/android/play/core/integrity/protocol/IIntegrityServiceCallback.aidl b/extensions/all/misc/disable-play-integrity/src/main/aidl/com/google/android/play/core/integrity/protocol/IIntegrityServiceCallback.aidl new file mode 100644 index 000000000..9485ec169 --- /dev/null +++ b/extensions/all/misc/disable-play-integrity/src/main/aidl/com/google/android/play/core/integrity/protocol/IIntegrityServiceCallback.aidl @@ -0,0 +1,7 @@ +package com.google.android.play.core.integrity.protocol; + +import android.os.Bundle; + +interface IIntegrityServiceCallback { + oneway void onResult(in Bundle result) = 1; +} diff --git a/extensions/all/misc/disable-play-integrity/src/main/java/android/ext/PackageId.java b/extensions/all/misc/disable-play-integrity/src/main/java/android/ext/PackageId.java new file mode 100644 index 000000000..31c2ca6db --- /dev/null +++ b/extensions/all/misc/disable-play-integrity/src/main/java/android/ext/PackageId.java @@ -0,0 +1,10 @@ +package android.ext; +/** @hide */ +// Int values that are assigned to packages in this interface can be retrieved at runtime from +// ApplicationInfo.ext().getPackageId() or from AndroidPackage.ext().getPackageId() (in system_server). +// +// PackageIds are assigned to parsed APKs only after they are verified, either by a certificate check +// or by a check that the APK is stored on an immutable OS partition. +public interface PackageId { + String PLAY_STORE_NAME = "com.android.vending"; +} diff --git a/extensions/all/misc/disable-play-integrity/src/main/java/android/os/BinderWrapper.java b/extensions/all/misc/disable-play-integrity/src/main/java/android/os/BinderWrapper.java new file mode 100644 index 000000000..a01806441 --- /dev/null +++ b/extensions/all/misc/disable-play-integrity/src/main/java/android/os/BinderWrapper.java @@ -0,0 +1,62 @@ +package android.os; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.io.FileDescriptor; + +/** @hide */ +public class BinderWrapper implements IBinder { + protected final IBinder base; + + public BinderWrapper(IBinder base) { + this.base = base; + } + + @Override + public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException { + return base.transact(code, data, reply, flags); + } + + @Nullable + @Override + public IInterface queryLocalInterface(@NonNull String descriptor) { + return base.queryLocalInterface(descriptor); + } + + @Nullable + @Override + public String getInterfaceDescriptor() throws RemoteException { + return base.getInterfaceDescriptor(); + } + + @Override + public boolean pingBinder() { + return base.pingBinder(); + } + + @Override + public boolean isBinderAlive() { + return base.isBinderAlive(); + } + + @Override + public void dump(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException { + base.dump(fd, args); + } + + @Override + public void dumpAsync(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException { + base.dumpAsync(fd, args); + } + + @Override + public void linkToDeath(@NonNull DeathRecipient recipient, int flags) throws RemoteException { + base.linkToDeath(recipient, flags); + } + + @Override + public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) { + return base.unlinkToDeath(recipient, flags); + } +} diff --git a/extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/playintegrity/ClassicPlayIntegrityServiceWrapper.java b/extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/playintegrity/ClassicPlayIntegrityServiceWrapper.java new file mode 100644 index 000000000..3bd88d2a6 --- /dev/null +++ b/extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/playintegrity/ClassicPlayIntegrityServiceWrapper.java @@ -0,0 +1,41 @@ +package app.grapheneos.gmscompat.lib.playintegrity; + +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; + +import com.android.internal.os.FakeBackgroundHandler; +import com.google.android.play.core.integrity.protocol.IIntegrityService; +import com.google.android.play.core.integrity.protocol.IIntegrityServiceCallback; + +class ClassicPlayIntegrityServiceWrapper extends PlayIntegrityServiceWrapper { + + ClassicPlayIntegrityServiceWrapper(IBinder base) { + super(base); + requestIntegrityTokenTxnCode = 2; // IIntegrityService.Stub.TRANSACTION_requestIntegrityToken + } + + static class TokenRequestStub extends IIntegrityService.Stub { + public void requestIntegrityToken(Bundle request, IIntegrityServiceCallback callback) { + Runnable r = () -> { + var result = new Bundle(); + // https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/model/IntegrityErrorCode.html#API_NOT_AVAILABLE + final int API_NOT_AVAILABLE = -1; + result.putInt("error", API_NOT_AVAILABLE); + try { + callback.onResult(result); + } catch (RemoteException e) { + Log.e("IIntegrityService.Stub", "", e); + } + }; + FakeBackgroundHandler.getHandler().postDelayed(r, getTokenRequestResultDelay()); + } + }; + + @Override + protected Binder createTokenRequestStub() { + return new TokenRequestStub(); + } +} diff --git a/extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/playintegrity/PlayIntegrityServiceWrapper.java b/extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/playintegrity/PlayIntegrityServiceWrapper.java new file mode 100644 index 000000000..0418b4fe7 --- /dev/null +++ b/extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/playintegrity/PlayIntegrityServiceWrapper.java @@ -0,0 +1,48 @@ +package app.grapheneos.gmscompat.lib.playintegrity; + +import android.os.Binder; +import android.os.BinderWrapper; +import android.os.IBinder; +import android.os.Parcel; +import android.os.RemoteException; +import android.util.Log; + +import androidx.annotation.Nullable; + +abstract class PlayIntegrityServiceWrapper extends BinderWrapper { + final String TAG; + protected int requestIntegrityTokenTxnCode; + + public PlayIntegrityServiceWrapper(IBinder base) { + super(base); + TAG = getClass().getSimpleName(); + } + + protected abstract Binder createTokenRequestStub(); + + @Override + public boolean transact(int code, Parcel data, @Nullable Parcel reply, int flags) throws RemoteException { + if (code == requestIntegrityTokenTxnCode) { + if (maybeStubOutIntegrityTokenRequest(code, data, reply, flags)) { + return true; + } + } + return super.transact(code, data, reply, flags); + } + + private boolean maybeStubOutIntegrityTokenRequest(int code, Parcel data, @Nullable Parcel reply, int flags) { + Log.d(TAG, "integrity token request detected"); + + try { + createTokenRequestStub().transact(code, data, reply, flags); + } catch (RemoteException e) { + // this is a local call + throw new IllegalStateException(e); + } + return true; + } + + protected static long getTokenRequestResultDelay() { + return 500L; + } +} diff --git a/extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/playintegrity/PlayIntegrityUtils.java b/extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/playintegrity/PlayIntegrityUtils.java new file mode 100644 index 000000000..6ff4720cc --- /dev/null +++ b/extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/playintegrity/PlayIntegrityUtils.java @@ -0,0 +1,35 @@ +package app.grapheneos.gmscompat.lib.playintegrity; + +import android.content.Intent; +import android.content.ServiceConnection; +import android.ext.PackageId; +import android.os.IBinder; +import androidx.annotation.Nullable; +import app.grapheneos.gmscompat.lib.util.ServiceConnectionWrapper; +import java.util.function.UnaryOperator; + +public class PlayIntegrityUtils { + + public static @Nullable ServiceConnection maybeReplaceServiceConnection(Intent service, ServiceConnection orig) { + if (PackageId.PLAY_STORE_NAME.equals(service.getPackage())) { + UnaryOperator binderOverride = null; + + final String CLASSIC_SERVICE = + "com.google.android.play.core.integrityservice.BIND_INTEGRITY_SERVICE"; + final String STANDARD_SERVICE = + "com.google.android.play.core.expressintegrityservice.BIND_EXPRESS_INTEGRITY_SERVICE"; + + String action = service.getAction(); + if (STANDARD_SERVICE.equals(action)) { + binderOverride = StandardPlayIntegrityServiceWrapper::new; + } else if (CLASSIC_SERVICE.equals(action)) { + binderOverride = ClassicPlayIntegrityServiceWrapper::new; + } + + if (binderOverride != null) { + return new ServiceConnectionWrapper(orig, binderOverride); + } + } + return null; + } +} diff --git a/extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/playintegrity/StandardPlayIntegrityServiceWrapper.java b/extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/playintegrity/StandardPlayIntegrityServiceWrapper.java new file mode 100644 index 000000000..c1c4937f0 --- /dev/null +++ b/extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/playintegrity/StandardPlayIntegrityServiceWrapper.java @@ -0,0 +1,42 @@ +package app.grapheneos.gmscompat.lib.playintegrity; + +import android.annotation.SuppressLint; +import android.os.Binder; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import com.android.internal.os.FakeBackgroundHandler; +import com.google.android.play.core.integrity.protocol.IExpressIntegrityService; +import com.google.android.play.core.integrity.protocol.IExpressIntegrityServiceCallback; + +@SuppressLint("LongLogTag") +class StandardPlayIntegrityServiceWrapper extends PlayIntegrityServiceWrapper { + + StandardPlayIntegrityServiceWrapper(IBinder base) { + super(base); + requestIntegrityTokenTxnCode = 3; // IExpressIntegrityService.Stub.TRANSACTION_requestIntegrityToken + } + + static class TokenRequestStub extends IExpressIntegrityService.Stub { + public void requestIntegrityToken(Bundle request, IExpressIntegrityServiceCallback callback) { + Runnable r = () -> { + var result = new Bundle(); + // https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/model/StandardIntegrityErrorCode.html#API_NOT_AVAILABLE + final int API_NOT_AVAILABLE = -1; + result.putInt("error", API_NOT_AVAILABLE); + try { + callback.onRequestExpressIntegrityTokenResult(result); + } catch (RemoteException e) { + Log.e("IExpressIntegrityService.Stub", "", e); + } + }; + FakeBackgroundHandler.getHandler().postDelayed(r, getTokenRequestResultDelay()); + } + }; + + @Override + protected Binder createTokenRequestStub() { + return new TokenRequestStub(); + } +} diff --git a/extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/util/ServiceConnectionWrapper.java b/extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/util/ServiceConnectionWrapper.java new file mode 100644 index 000000000..9edfc39f8 --- /dev/null +++ b/extensions/all/misc/disable-play-integrity/src/main/java/app/grapheneos/gmscompat/lib/util/ServiceConnectionWrapper.java @@ -0,0 +1,49 @@ +package app.grapheneos.gmscompat.lib.util; + +import android.content.ComponentName; +import android.content.ServiceConnection; +import android.os.Build; +import android.os.IBinder; + +import java.util.function.UnaryOperator; + +public class ServiceConnectionWrapper implements ServiceConnection { + private final ServiceConnection base; + private final UnaryOperator binderOverride; + + public ServiceConnectionWrapper(ServiceConnection base, UnaryOperator binderOverride) { + this.base = base; + this.binderOverride = binderOverride; + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + IBinder override = binderOverride.apply(service); + if (override != null) { + service = override; + } + } + + base.onServiceConnected(name, service); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + base.onServiceDisconnected(name); + } + + @Override + public void onBindingDied(ComponentName name) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + base.onBindingDied(name); + } + } + + @Override + public void onNullBinding(ComponentName name) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + base.onNullBinding(name); + } + } +} diff --git a/extensions/all/misc/disable-play-integrity/src/main/java/app/revanced/extension/playintegrity/DisablePlayIntegrityPatch.java b/extensions/all/misc/disable-play-integrity/src/main/java/app/revanced/extension/playintegrity/DisablePlayIntegrityPatch.java new file mode 100644 index 000000000..a27e56be9 --- /dev/null +++ b/extensions/all/misc/disable-play-integrity/src/main/java/app/revanced/extension/playintegrity/DisablePlayIntegrityPatch.java @@ -0,0 +1,17 @@ +package app.revanced.extension.playintegrity; + +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import app.grapheneos.gmscompat.lib.playintegrity.PlayIntegrityUtils; + +public class DisablePlayIntegrityPatch { + public static boolean bindService(Context context, Intent service, ServiceConnection conn, int flags) { + ServiceConnection override = PlayIntegrityUtils.maybeReplaceServiceConnection(service, conn); + if (override != null) { + conn = override; + } + + return context.bindService(service, conn, flags); + } +} diff --git a/extensions/all/misc/disable-play-integrity/src/main/java/com/android/internal/os/FakeBackgroundHandler.java b/extensions/all/misc/disable-play-integrity/src/main/java/com/android/internal/os/FakeBackgroundHandler.java new file mode 100644 index 000000000..6b4cb92b4 --- /dev/null +++ b/extensions/all/misc/disable-play-integrity/src/main/java/com/android/internal/os/FakeBackgroundHandler.java @@ -0,0 +1,11 @@ +package com.android.internal.os; + +import android.os.Handler; +import android.os.Looper; + +public class FakeBackgroundHandler { + + public static Handler getHandler() { + return new Handler(Looper.getMainLooper()); + } +} diff --git a/patches/api/patches.api b/patches/api/patches.api index 9b5ac4d0c..ec75df2b3 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -104,6 +104,10 @@ public final class app/revanced/patches/all/misc/packagename/ChangePackageNamePa public static final fun setPackageNameOption (Lapp/revanced/patcher/patch/Option;)V } +public final class app/revanced/patches/all/misc/playintegrity/DisablePlayIntegrityKt { + public static final fun getDisablePlayIntegrityPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + public final class app/revanced/patches/all/misc/resources/AddResourcesPatchKt { public static final fun addResource (Ljava/lang/String;Lapp/revanced/util/resource/BaseResource;)Z public static final fun addResources (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;)Z diff --git a/patches/src/main/kotlin/app/revanced/patches/all/misc/playintegrity/DisablePlayIntegrity.kt b/patches/src/main/kotlin/app/revanced/patches/all/misc/playintegrity/DisablePlayIntegrity.kt new file mode 100644 index 000000000..25a948e34 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/all/misc/playintegrity/DisablePlayIntegrity.kt @@ -0,0 +1,55 @@ +package app.revanced.patches.all.misc.playintegrity + +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.transformation.transformInstructionsPatch +import app.revanced.util.getReference +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c +import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference +import com.android.tools.smali.dexlib2.util.MethodUtil + +private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/playintegrity/DisablePlayIntegrityPatch;" + +private val CONTEXT_BIND_SERVICE_METHOD_REFERENCE = ImmutableMethodReference( + "Landroid/content/Context;", + "bindService", + listOf("Landroid/content/Intent;", "Landroid/content/ServiceConnection;", "I"), + "Z" +) + + +@Suppress("unused") +val disablePlayIntegrityPatch = bytecodePatch( + name = "Disable Play Integrity", + description = "Prevents apps from using Play Integrity by pretending it is not available.", + use = false, +) { + extendWith("extensions/all/misc/disable-play-integrity.rve") + + dependsOn( + transformInstructionsPatch( + filterMap = filterMap@{ classDef, method, instruction, instructionIndex -> + val reference = instruction + .getReference() + ?.takeIf { + MethodUtil.methodSignaturesMatch(CONTEXT_BIND_SERVICE_METHOD_REFERENCE, it) + } + ?: return@filterMap null + + Triple(instruction as Instruction35c, instructionIndex, reference.parameterTypes) + }, + transform = { method, entry -> + val (instruction, index, parameterTypes) = entry + val parameterString = parameterTypes.joinToString(separator = "") + val registerString = "v${instruction.registerC}, v${instruction.registerD}, v${instruction.registerE}, v${instruction.registerF}" + + method.replaceInstruction( + index, + "invoke-static { $registerString }, $EXTENSION_CLASS_DESCRIPTOR->bindService(Landroid/content/Context;$parameterString)Z" + ) + } + ) + ) +}