From dc1d04e883390fa1d82cda832df81f375cd5675e Mon Sep 17 00:00:00 2001 From: JingMatrix Date: Mon, 1 Sep 2025 17:22:59 +0200 Subject: [PATCH] Support Android 16 (Baklava) Refactors the screen capture hooking logic to support major API changes introduced in Android 16. The previous implementation failed on newer platforms due to class, method, and field refactoring in the AOSP framework. This commit introduces version-aware logic to handle three distinct generations of the screen capture API: - **Android 13 and below:** Continues to use the original hook for `android.view.SurfaceControl` and its boolean `mCaptureSecureLayers` field. - **Android 14 (Upside Down Cake):** Targets the new `android.window.ScreenCapture` class and its `CaptureArgs`, which still uses the `mCaptureSecureLayers` field. - **Android Baklava (API 36+):** Adapts to the latest API, which includes: - Targeting the new `android.window.ScreenCaptureInternal` class. - Hooking the renamed methods `captureDisplay` and `captureLayers` (dropping the "native" prefix). - Modifying the new integer-based policy fields `mSecureContentPolicy` and `mProtectedContentPolicy` by setting them to `1` to enable capture. --- .../disableflagsecure/DisableFlagSecure.java | 51 ++++++++++++++----- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/matrix/disableflagsecure/DisableFlagSecure.java b/app/src/main/java/org/matrix/disableflagsecure/DisableFlagSecure.java index 1970969..cb7dabf 100644 --- a/app/src/main/java/org/matrix/disableflagsecure/DisableFlagSecure.java +++ b/app/src/main/java/org/matrix/disableflagsecure/DisableFlagSecure.java @@ -228,19 +228,38 @@ private void hookWindowState(ClassLoader classLoader) throws ClassNotFoundExcept hook(isSecureLockedMethod, SecureLockedHooker.class); } - private static Field captureSecureLayersField; + private static Field captureSecureLayersField; // For Android < 16 + private static Field secureContentPolicyField; // For Android 16+ + private static Field protectedContentPolicyField; // For Android 16+ private void hookScreenCapture(ClassLoader classLoader) throws ClassNotFoundException, NoSuchFieldException { - var screenCaptureClazz = Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE ? - classLoader.loadClass("android.window.ScreenCapture") : - SurfaceControl.class; - var captureArgsClazz = classLoader.loadClass(Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE ? - "android.window.ScreenCapture$CaptureArgs" : - "android.view.SurfaceControl$CaptureArgs"); - captureSecureLayersField = captureArgsClazz.getDeclaredField("mCaptureSecureLayers"); - captureSecureLayersField.setAccessible(true); - hookMethods(screenCaptureClazz, ScreenCaptureHooker.class, "nativeCaptureDisplay"); - hookMethods(screenCaptureClazz, ScreenCaptureHooker.class, "nativeCaptureLayers"); + Class screenCaptureClazz; + String captureArgsClassName; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) { + captureArgsClassName = "android.window.ScreenCaptureInternal$CaptureArgs"; + screenCaptureClazz = classLoader.loadClass("android.window.ScreenCaptureInternal"); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + captureArgsClassName = "android.window.ScreenCapture$CaptureArgs"; + screenCaptureClazz = classLoader.loadClass("android.window.ScreenCapture"); + } else { + captureArgsClassName = "android.view.SurfaceControl$CaptureArgs"; + screenCaptureClazz = SurfaceControl.class; + } + var captureArgsClazz = classLoader.loadClass(captureArgsClassName); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) { + secureContentPolicyField = captureArgsClazz.getDeclaredField("mSecureContentPolicy"); + secureContentPolicyField.setAccessible(true); + protectedContentPolicyField = captureArgsClazz.getDeclaredField("mProtectedContentPolicy"); + protectedContentPolicyField.setAccessible(true); + hookMethods(screenCaptureClazz, ScreenCaptureHooker.class, "captureDisplay"); + hookMethods(screenCaptureClazz, ScreenCaptureHooker.class, "captureLayers"); + } else { + captureSecureLayersField = captureArgsClazz.getDeclaredField("mCaptureSecureLayers"); + captureSecureLayersField.setAccessible(true); + hookMethods(screenCaptureClazz, ScreenCaptureHooker.class, "nativeCaptureDisplay"); + hookMethods(screenCaptureClazz, ScreenCaptureHooker.class, "nativeCaptureLayers"); + } } private void hookDisplayControl(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException { @@ -373,7 +392,15 @@ private static class ScreenCaptureHooker implements Hooker { public static void before(@NonNull BeforeHookCallback callback) { var captureArgs = callback.getArgs()[0]; try { - captureSecureLayersField.set(captureArgs, true); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) { + module.log("captureArgs: [secureContentPolicy, protectedContentPolicy]: [" + + secureContentPolicyField.get(captureArgs) + ", " + + protectedContentPolicyField.get(captureArgs) + "]"); + secureContentPolicyField.set(captureArgs, 1); + protectedContentPolicyField.set(captureArgs, 1); + } else { + captureSecureLayersField.set(captureArgs, true); + } } catch (IllegalAccessException t) { module.log("ScreenCaptureHooker failed", t); }