Compare commits

..

20 Commits
v4.1.0 ... main

Author SHA1 Message Date
tehcneko
b64046f57c update compile sdk to 36 2025-04-05 12:57:11 +08:00
tehcneko
bc17c4c9bb update ci to jdk 21 2025-04-05 12:57:11 +08:00
tehcneko
401f196e2e check caller method with stack walker 2025-04-05 12:57:11 +08:00
tehcneko
bf5cdb513d replace @TargetApi with @RequireApi 2025-04-05 12:57:11 +08:00
tehcneko
f11e16da7d update AGP 2025-04-05 12:57:11 +08:00
tehcneko
de591e925e
fix issue template 2025-04-05 12:49:42 +08:00
tehcneko
4b426733ca bump version 2025-03-04 19:49:27 +08:00
tehcneko
a5461af177 support scrcpy 2025-03-04 19:48:49 +08:00
tehcneko
aa56f32b6a update issue template 2025-03-04 19:35:08 +08:00
tehcneko
cf19309b6b add support info to README 2025-03-04 19:30:47 +08:00
tehcneko
cd14a26f54 remove support for Android 11- 2025-03-04 19:10:58 +08:00
tehcneko
20c7f4c30d update gradle 2025-03-04 18:55:48 +08:00
tehcneko
624a493311 update gradle 2025-01-12 11:56:00 +08:00
Wang Han
a5e8659290
Bump version 2024-12-02 11:00:19 +08:00
Wang Han
375469dd23
Hook OplusScreenCapture#setUid on Oplus A15 (#75)
Or surfaceflinger will silently reject screen capture after checking uid.
2024-11-30 21:09:03 +08:00
dependabot[bot]
2baedef66f
Bump com.android.application from 8.6.1 to 8.7.2 (#72) 2024-11-30 16:53:10 +08:00
dependabot[bot]
469d58c95f
Bump androidx.annotation:annotation from 1.8.2 to 1.9.1 (#71) 2024-11-30 16:49:55 +08:00
tehcneko
e0203a480f update gradle 2024-09-29 21:08:34 +08:00
dependabot[bot]
197f971cbb
Bump com.android.application from 8.5.2 to 8.6.0 (#62) 2024-08-30 08:57:44 +08:00
tehcneko
9fbc536136 fix SOURCE_URL 2024-08-19 18:04:32 +08:00
13 changed files with 141 additions and 102 deletions

View File

@ -29,6 +29,13 @@ body:
options:
- label: I am using latest debug CI version
required: true
- type: checkboxes
id: official
attributes:
label: LSPosed requirement
options:
- label: I am using official LSPosed version
required: true
- type: textarea
attributes:
label: Behavior

View File

@ -23,10 +23,10 @@ jobs:
path: libxposed/api
fetch-depth: 0
- name: set up JDK 17
- name: set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '17'
java-version: '21'
distribution: 'temurin'
cache: gradle
@ -50,7 +50,6 @@ jobs:
echo 'org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 -XX:+UseParallelGC' >> ~/.gradle/gradle.properties
echo 'android.native.buildOutput=verbose' >> ~/.gradle/gradle.properties
# build dependencies
sed -i 's/VERSION_21/VERSION_17/g' libxposed/api/checks/build.gradle.kts
cd libxposed/api && ./gradlew publishToMavenLocal && cd ../..
# build DisableFlagSecure
chmod +x gradlew

View File

@ -1,6 +1,14 @@
# Enable Screenshot (formerly known as Disable FLAG_SECURE)
Enabling screenshots in apps that normally wouldn\'t allow it, and disabling screenshot(Android 14+) & screen record(Android 15+) detection.
**Unofficial LSPosed versions are not supported.**
## Supported OSes
- Android 12-16 Beta 3 (Custom ROMs are **not** supported)
- Xiaomi Hyper OS
- OPlus OS (Color OS/Realme UI/Oxygen OS)
- Samsung One UI
## Usage
1. Enable the module
2. Select **ONLY** recommended apps

1
SOURCE
View File

@ -1 +0,0 @@
https://github.com/Xposed-Modules-Repo/io.github.lsposed.disableflagsecure

1
SOURCE_URL Normal file
View File

@ -0,0 +1 @@
https://github.com/LSPosed/DisableFlagSecure

View File

@ -3,15 +3,15 @@ plugins {
}
android {
compileSdk 35
buildToolsVersion "35.0.0"
compileSdk 36
buildToolsVersion "36.0.0"
defaultConfig {
applicationId "io.github.lsposed.disableflagsecure"
minSdkVersion 27
targetSdkVersion 35
minSdkVersion 31
targetSdkVersion 36
versionCode rootProject.ext.commitCount
versionName "4.1.0"
versionName "4.2.0"
}
Properties localProperties = new Properties()
@ -42,14 +42,15 @@ android {
} else {
signingConfig signingConfigs.debug
}
vcsInfo.include false
minifyEnabled true
proguardFiles 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
sourceCompatibility JavaVersion.VERSION_21
targetCompatibility JavaVersion.VERSION_21
}
lint {
@ -61,7 +62,7 @@ android {
}
dependencies {
compileOnly 'androidx.annotation:annotation:1.8.2'
compileOnly 'androidx.annotation:annotation:1.9.1'
compileOnly 'io.github.libxposed:api:100'
compileOnly project(":libxposed-compat")
}

View File

@ -1,18 +1,16 @@
package io.github.lsposed.disableflagsecure;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.hardware.display.DisplayManager;
import android.os.Build;
import android.util.Log;
import android.view.SurfaceControl;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.function.BiConsumer;
import java.util.function.BiPredicate;
@ -26,6 +24,7 @@ import io.github.libxposed.api.annotations.XposedHooker;
public class DisableFlagSecure extends XposedModule {
private static final String SYSTEMUI = "com.android.systemui";
private static final String OPLUS_APPPLATFORM = "com.oplus.appplatform";
private static final String OPLUS_SCREENSHOT = "com.oplus.screenshot";
private static final String FLYME_SYSTEMUIEX = "com.flyme.systemuiex";
private static final String MIUI_SCREENSHOT = "com.miui.screenshot";
@ -47,7 +46,7 @@ public class DisableFlagSecure extends XposedModule {
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
// Screen record detection (V)
// Screen record detection (V~Baklava)
try {
hookWindowManagerService(classLoader);
} catch (Throwable t) {
@ -56,14 +55,15 @@ public class DisableFlagSecure extends XposedModule {
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
// Screenshot detection (U~V)
// Screenshot detection (U~Baklava)
try {
hookActivityTaskManagerService(classLoader);
} catch (Throwable t) {
log("hook ActivityTaskManagerService failed", t);
}
// Xiaomi HyperOS (U)
// Xiaomi HyperOS (U~Baklava)
// OS2.0.250220.1.WOCCNXM.PRE
try {
hookHyperOS(classLoader);
} catch (ClassNotFoundException ignored) {
@ -72,8 +72,7 @@ public class DisableFlagSecure extends XposedModule {
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// ScreenCapture in WindowManagerService (S~V)
// ScreenCapture in WindowManagerService (S~Baklava)
try {
hookScreenCapture(classLoader);
} catch (Throwable t) {
@ -89,22 +88,20 @@ public class DisableFlagSecure extends XposedModule {
}
}
// WifiDisplay (S~V) / OverlayDisplay (S~V) / VirtualDisplay (U~V)
// WifiDisplay (S~Baklava) / OverlayDisplay (S~Baklava) / VirtualDisplay (U~Baklava)
try {
hookDisplayControl(classLoader);
} catch (Throwable t) {
log("hook DisplayControl failed", t);
}
// VirtualDisplay with MediaProjection (S~V)
// VirtualDisplay with MediaProjection (S~Baklava)
try {
hookVirtualDisplayAdapter(classLoader);
} catch (Throwable t) {
log("hook VirtualDisplayAdapter failed", t);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// OneUI
try {
hookScreenshotHardwareBuffer(classLoader);
@ -120,9 +117,8 @@ public class DisableFlagSecure extends XposedModule {
log("hook OneUI failed", t);
}
}
}
// secureLocked flag (S-)
// secureLocked flag
try {
// Screenshot
hookWindowState(classLoader);
@ -134,8 +130,10 @@ public class DisableFlagSecure extends XposedModule {
// dumpsys window screenshot systemQuickTileScreenshotOut display_id=0
try {
hookOplus(classLoader);
} catch (Throwable ignored) {
} catch (Throwable t) {
if (!(t instanceof ClassNotFoundException)) {
log("hook Oplus failed", t);
}
}
}
@ -147,6 +145,17 @@ public class DisableFlagSecure extends XposedModule {
var classLoader = param.getClassLoader();
var pn = param.getPackageName();
switch (pn) {
case OPLUS_SCREENSHOT:
// Oplus Screenshot 15.0.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
try {
hookOplusScreenCapture(classLoader);
} catch (Throwable t) {
if (!(t instanceof ClassNotFoundException)) {
log("hook OplusScreenCapture failed", t);
}
}
}
case FLYME_SYSTEMUIEX:
case OPLUS_APPPLATFORM:
// Flyme SystemUI Ext 10.3.0
@ -160,10 +169,10 @@ public class DisableFlagSecure extends XposedModule {
}
case SYSTEMUI:
case MIUI_SCREENSHOT:
if (OPLUS_APPPLATFORM.equals(pn) ||
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE)) {
// ScreenCapture in App (S~T) (OPlus S-U)
if (OPLUS_APPPLATFORM.equals(pn) || OPLUS_SCREENSHOT.equals(pn) ||
Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
// ScreenCapture in App (S~T) (OPlus S~V)
// TODO: test Oplus Baklava
try {
hookScreenCapture(classLoader);
} catch (Throwable t) {
@ -215,19 +224,12 @@ public class DisableFlagSecure extends XposedModule {
private void hookWindowState(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException {
var windowStateClazz = classLoader.loadClass("com.android.server.wm.WindowState");
Method isSecureLockedMethod;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
isSecureLockedMethod = windowStateClazz.getDeclaredMethod("isSecureLocked");
} else {
var windowManagerServiceClazz = classLoader.loadClass("com.android.server.wm.WindowManagerService");
isSecureLockedMethod = windowManagerServiceClazz.getDeclaredMethod("isSecureLocked", windowStateClazz);
}
var isSecureLockedMethod = windowStateClazz.getDeclaredMethod("isSecureLocked");
hook(isSecureLockedMethod, SecureLockedHooker.class);
}
private static Field captureSecureLayersField;
@TargetApi(Build.VERSION_CODES.S)
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") :
@ -241,7 +243,6 @@ public class DisableFlagSecure extends XposedModule {
hookMethods(screenCaptureClazz, ScreenCaptureHooker.class, "nativeCaptureLayers");
}
@TargetApi(Build.VERSION_CODES.S)
private void hookDisplayControl(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException {
var displayControlClazz = Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE ?
classLoader.loadClass("com.android.server.display.DisplayControl") :
@ -253,13 +254,12 @@ public class DisableFlagSecure extends XposedModule {
hook(method, CreateDisplayHooker.class);
}
@TargetApi(Build.VERSION_CODES.S)
private void hookVirtualDisplayAdapter(ClassLoader classLoader) throws ClassNotFoundException {
var displayControlClazz = classLoader.loadClass("com.android.server.display.VirtualDisplayAdapter");
hookMethods(displayControlClazz, CreateVirtualDisplayLockedHooker.class, "createVirtualDisplayLocked");
}
@TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
private void hookActivityTaskManagerService(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException {
var activityTaskManagerServiceClazz = classLoader.loadClass("com.android.server.wm.ActivityTaskManagerService");
var iBinderClazz = classLoader.loadClass("android.os.IBinder");
@ -268,7 +268,7 @@ public class DisableFlagSecure extends XposedModule {
hook(method, ReturnNullHooker.class);
}
@TargetApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
private void hookWindowManagerService(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException {
var windowManagerServiceClazz = classLoader.loadClass("com.android.server.wm.WindowManagerService");
var iScreenRecordingCallbackClazz = classLoader.loadClass("android.window.IScreenRecordingCallback");
@ -276,20 +276,18 @@ public class DisableFlagSecure extends XposedModule {
hook(method, ReturnFalseHooker.class);
}
@TargetApi(Build.VERSION_CODES.S)
private void hookActivityManagerService(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException {
var activityTaskManagerServiceClazz = classLoader.loadClass("com.android.server.am.ActivityManagerService");
var method = activityTaskManagerServiceClazz.getDeclaredMethod("checkPermission", String.class, int.class, int.class);
hook(method, CheckPermissionHooker.class);
}
@TargetApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
private void hookHyperOS(ClassLoader classLoader) throws ClassNotFoundException {
var windowManagerServiceImplClazz = classLoader.loadClass("com.android.server.wm.WindowManagerServiceImpl");
hookMethods(windowManagerServiceImplClazz, ReturnFalseHooker.class, "notAllowCaptureDisplay");
}
@TargetApi(Build.VERSION_CODES.S)
private void hookScreenshotHardwareBuffer(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException {
var screenshotHardwareBufferClazz = classLoader.loadClass(
Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE ?
@ -299,13 +297,19 @@ public class DisableFlagSecure extends XposedModule {
hook(method, ReturnFalseHooker.class);
}
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
private void hookOplusScreenCapture(ClassLoader classLoader) throws ClassNotFoundException, NoSuchMethodException {
var oplusScreenCaptureClazz = classLoader.loadClass("com.oplus.screenshot.OplusScreenCapture$CaptureArgs$Builder");
var method = oplusScreenCaptureClazz.getDeclaredMethod("setUid", long.class);
hook(method, OplusScreenCaptureHooker.class);
}
private void hookOplus(ClassLoader classLoader) throws ClassNotFoundException {
// caller: com.android.server.wm.OplusLongshotWindowDump#dumpWindows
var longshotMainClazz = classLoader.loadClass("com.android.server.wm.OplusLongshotMainWindow");
hookMethods(longshotMainClazz, ReturnFalseHooker.class, "hasSecure");
}
@TargetApi(Build.VERSION_CODES.S)
private void hookOneUI(ClassLoader classLoader) throws ClassNotFoundException {
var wmScreenshotControllerClazz = classLoader.loadClass("com.android.server.wm.WmScreenshotController");
hookMethods(wmScreenshotControllerClazz, ReturnTrueHooker.class, "canBeScreenshotTarget");
@ -329,11 +333,14 @@ public class DisableFlagSecure extends XposedModule {
@BeforeInvocation
public static void before(@NonNull BeforeHookCallback callback) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
String stack = Log.getStackTraceString(new Throwable());
if (stack.contains("createVirtualDisplayLocked")) {
var stackTrace = new Throwable().getStackTrace();
for (int i = 4; i < stackTrace.length && i < 8; i++) {
var name = stackTrace[i].getMethodName();
if (name.equals("createVirtualDisplayLocked")) {
return;
}
}
}
callback.getArgs()[1] = true;
}
}
@ -350,6 +357,15 @@ public class DisableFlagSecure extends XposedModule {
}
}
@XposedHooker
private static class OplusScreenCaptureHooker implements Hooker {
@BeforeInvocation
public static void before(@NonNull BeforeHookCallback callback) {
callback.getArgs()[0] = -1;
}
}
@XposedHooker
private static class ScreenCaptureHooker implements Hooker {
@ -370,7 +386,7 @@ public class DisableFlagSecure extends XposedModule {
@BeforeInvocation
public static void before(@NonNull BeforeHookCallback callback) {
var caller = (int) callback.getArgs()[2];
if (caller != 1000 && callback.getArgs()[1] == null) {
if (caller >= 10000 && callback.getArgs()[1] == null) {
// not os and not media projection
return;
}
@ -392,14 +408,24 @@ public class DisableFlagSecure extends XposedModule {
@BeforeInvocation
public static void before(@NonNull BeforeHookCallback callback) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
String stack = Log.getStackTraceString(new Throwable());
// don't change surface flags, but passing other checks
if (stack.contains("setInitialSurfaceControlProperties")
|| stack.contains("createSurfaceLocked")) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
var walker = StackWalker.getInstance();
var match = walker.walk(frames -> frames
.map(StackWalker.StackFrame::getMethodName)
.limit(6)
.skip(2)
.anyMatch(s -> s.equals("setInitialSurfaceControlProperties") || s.equals("createSurfaceLocked")));
if (match) return;
} else {
var stackTrace = new Throwable().getStackTrace();
for (int i = 4; i < stackTrace.length && i < 8; i++) {
var name = stackTrace[i].getMethodName();
if (name.equals("setInitialSurfaceControlProperties") ||
name.equals("createSurfaceLocked")) {
return;
}
}
}
callback.returnAndSkip(false);
}
}

View File

@ -3,3 +3,4 @@ com.android.systemui
com.flyme.systemuiex
com.miui.screenshot
com.oplus.appplatform
com.oplus.screenshot

View File

@ -1,10 +1,10 @@
plugins {
id 'com.android.application' version '8.5.2' apply false
id 'com.android.application' version '8.9.1' apply false
id 'org.lsposed.lsplugin.jgit' version "1.1"
}
tasks.register('clean', Delete) {
delete rootProject.buildDir
delete rootProject.layout.buildDirectory
}
var repo = jgit.repo(true)

Binary file not shown.

View File

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME

5
gradlew vendored
View File

@ -86,8 +86,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@ -206,7 +205,7 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.

View File

@ -9,12 +9,10 @@ android {
defaultConfig {
minSdk 24
targetSdk 35
versionCode 1
versionName "1.0"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
sourceCompatibility JavaVersion.VERSION_21
targetCompatibility JavaVersion.VERSION_21
}
}