Compare commits

...

14 Commits

Author SHA1 Message Date
Simon Chan
330a9b1ec9 Fix AudioRecord package name for Android 16
Since commit 9f91a5eebb4520b9333576e946b3911d0f946a04 in frameworks/av
(AOSP), an AudioRecord can be created only if the declared package name
in the AttributionSource is "shell" (for the shell UID):
 - <7c4e6991ac/services/audiopolicy/permission/NativePermissionController.cpp (129)>
 - <7c4e6991ac/services/audiopolicy/permission/NativePermissionController.cpp (40)>

Refs 9f91a5eebb%5E%21/
Fixes #5698 <https://github.com/Genymobile/scrcpy/issues/5698>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2024-12-23 12:17:12 +01:00
Simon Chan
5b1229a55f Support older macOS versions in CI build
Fixes #5649 <https://github.com/Genymobile/scrcpy/issues/5649>
Fixes #5697<https://github.com/Genymobile/scrcpy/pull/5697>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2024-12-23 11:58:06 +01:00
Romain Vimont
69858c6f43 Build static linux binary on Ubuntu 20.04
Use the oldest Ubuntu version currently available in GitHub Actions to
ensure maximum compatibility with older systems.

Refs 95c4f03c1bfd566b383780977b3473ebad6477ee
Refs #5689 <https://github.com/Genymobile/scrcpy/issues/5689>
2024-12-23 11:01:42 +01:00
Romain Vimont
e0423653c8 Remove useless null check
The method CameraManager.getCameraIdList() is annotated with @NonNull.

This fixes a warning reported by Android Studio.
2024-12-23 10:58:59 +01:00
Romain Vimont
5387644160 Ignore low-FPS ranges if not available
Do not report an error if the returned FPS ranges array is null.

Refs #5669 <https://github.com/Genymobile/scrcpy/pull/5669>
2024-12-22 21:17:51 +01:00
Simon Chan
2f44da76f4 Filter out non-backward-compatible cameras
PR #5669 <https://github.com/Genymobile/scrcpy/pull/5669>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2024-12-22 21:17:05 +01:00
Romain Vimont
95c4f03c1b Build static linux binary on Ubuntu 22.04
On Github Actions, ubuntu-latest now points to ubuntu-24.04, which uses
a newer version of glibc (2.39). As a result, the binaries fail to work
on systems with older versions of glibc, such as Debian Bookworm.

To ensure better compatibility, continue building the static Linux
binary on Ubuntu 22.04 (with glibc 2.35).

Fixes #5689 <https://github.com/Genymobile/scrcpy/issues/5689>
2024-12-22 15:49:46 +01:00
Romain Vimont
fb47b87eeb Fix pipe read return value
The function incorrectly returned false, whereas its return type is
ssize_t.
2024-12-20 20:57:20 +01:00
Romain Vimont
dc2fcc46f5 Add workaround for Pico 4 Ultra
Make ActivityThread.isSystem() return true to avoid a
NullPointerException later.

Refs #5659 comment <https://github.com/Genymobile/scrcpy/issues/5659#issuecomment-2540963953>
Fixes #5659 <https://github.com/Genymobile/scrcpy/issues/5659>
2024-12-14 10:27:38 +01:00
Romain Vimont
69264703b1 Add missing comments in workarounds
The implementation of workarounds uses a lot of reflection code. For
better readability, always write the equivalent using direct Java code.
2024-12-14 10:27:38 +01:00
Colin Kinloch
ec4e826976 Set icon and server env paths for meson devenv
This allows users to compile and run the project in a dev environment.

    meson setup x
    meson compile -C x
    meson devenv -C x
    scrcpy

This is an alternative to `./run x`.

PR #5658 <https://github.com/Genymobile/scrcpy/pull/5658>

Signed-off-by: Romain Vimont <rom@rom1v.com>
2024-12-12 18:15:26 +01:00
Romain Vimont
17e205e54f Replace meson join_paths() by '/'
A new '/' operator was introduced in Meson 0.49 to replace join_paths():
 - <https://mesonbuild.com/Reference-manual_functions.html#join_paths>
 - <https://mesonbuild.com/Syntax.html#string-path-building>

Refs #5658 <https://github.com/Genymobile/scrcpy/pull/5658>
2024-12-12 18:15:26 +01:00
Romain Vimont
f751274b17 Define both pkg-config and pkgconfig for meson
In Meson cross-files, "pkgconfig" was deprecated in favor of
"pkg-config" in meson 1.3.0.

The new name is used since 85a94dd4b563e961304b2d9082932c5c1cc2e582 to
avoid a warning, but then it fails with older versions of meson.

To avoid the problem, define both pkg-config and pkgconfig.

> For backward compatibility it is still allowed to define both with the
> same value, in that case no deprecation warning is printed.

<https://mesonbuild.com/Release-notes-for-1-3-0.html#machine-files-pkgconfig-field-deprecated-and-replaced-by-pkgconfig>
2024-12-12 18:09:31 +01:00
Romain Vimont
6469054b15 Revert "Remove apt update on GitHub Actions"
This reverts commit 678025b31672c230575fe2dbc4a0d487d5010bb1.

This avoids spurious errors on the CI:

    E: Unable to fetch some archives, maybe run apt-get update or try
    with --fix-missing?
2024-12-12 18:09:31 +01:00
10 changed files with 80 additions and 20 deletions

View File

@ -74,6 +74,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt update
sudo apt install -y meson ninja-build nasm ffmpeg libsdl2-2.0-0 \ sudo apt install -y meson ninja-build nasm ffmpeg libsdl2-2.0-0 \
libsdl2-dev libavcodec-dev libavdevice-dev libavformat-dev \ libsdl2-dev libavcodec-dev libavdevice-dev libavformat-dev \
libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \ libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \
@ -83,7 +84,7 @@ jobs:
run: release/test_client.sh run: release/test_client.sh
build-linux-x86_64: build-linux-x86_64:
runs-on: ubuntu-latest runs-on: ubuntu-20.04
steps: steps:
- name: Check architecture - name: Check architecture
run: | run: |
@ -99,6 +100,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt update
sudo apt install -y meson ninja-build nasm ffmpeg libsdl2-2.0-0 \ sudo apt install -y meson ninja-build nasm ffmpeg libsdl2-2.0-0 \
libsdl2-dev libavcodec-dev libavdevice-dev libavformat-dev \ libsdl2-dev libavcodec-dev libavdevice-dev libavformat-dev \
libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \ libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \
@ -129,14 +131,12 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt update
sudo apt install -y meson ninja-build nasm ffmpeg libsdl2-2.0-0 \ sudo apt install -y meson ninja-build nasm ffmpeg libsdl2-2.0-0 \
libsdl2-dev libavcodec-dev libavdevice-dev libavformat-dev \ libsdl2-dev libavcodec-dev libavdevice-dev libavformat-dev \
libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \ libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \
mingw-w64 mingw-w64-tools libz-mingw-w64-dev mingw-w64 mingw-w64-tools libz-mingw-w64-dev
- name: Workaround for old meson version run by Github Actions
run: sed -i 's/^pkg-config/pkgconfig/' cross_win32.txt
- name: Build - name: Build
run: release/build_windows.sh 32 run: release/build_windows.sh 32
@ -162,14 +162,12 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt update
sudo apt install -y meson ninja-build nasm ffmpeg libsdl2-2.0-0 \ sudo apt install -y meson ninja-build nasm ffmpeg libsdl2-2.0-0 \
libsdl2-dev libavcodec-dev libavdevice-dev libavformat-dev \ libsdl2-dev libavcodec-dev libavdevice-dev libavformat-dev \
libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \ libavutil-dev libswresample-dev libusb-1.0-0 libusb-1.0-0-dev \
mingw-w64 mingw-w64-tools libz-mingw-w64-dev mingw-w64 mingw-w64-tools libz-mingw-w64-dev
- name: Workaround for old meson version run by Github Actions
run: sed -i 's/^pkg-config/pkgconfig/' cross_win64.txt
- name: Build - name: Build
run: release/build_windows.sh 64 run: release/build_windows.sh 64
@ -208,6 +206,13 @@ jobs:
libtool libtool
- name: Build - name: Build
env:
# the default Xcode (and macOS SDK) version can be found at
# <https://github.com/actions/runner-images/blob/main/images/macos/macos-15-Readme.md#xcode>
#
# then the minimal supported deployment target of that macOS SDK can be found at
# <https://developer.apple.com/support/xcode/#minimum-requirements>
MACOSX_DEPLOYMENT_TARGET: 10.13
run: release/build_macos.sh aarch64 run: release/build_macos.sh aarch64
# upload-artifact does not preserve permissions # upload-artifact does not preserve permissions
@ -244,6 +249,13 @@ jobs:
# autoconf and libtool are already installed on macos-13 # autoconf and libtool are already installed on macos-13
- name: Build - name: Build
env:
# the default Xcode (and macOS SDK) version can be found at
# <https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md#xcode>
#
# then the minimal supported deployment target of that macOS SDK can be found at
# <https://developer.apple.com/support/xcode/#minimum-requirements>
MACOSX_DEPLOYMENT_TARGET: 10.13
run: release/build_macos.sh x86_64 run: release/build_macos.sh x86_64
# upload-artifact does not preserve permissions # upload-artifact does not preserve permissions

View File

@ -192,19 +192,19 @@ datadir = get_option('datadir') # by default 'share'
install_man('scrcpy.1') install_man('scrcpy.1')
install_data('data/icon.png', install_data('data/icon.png',
rename: 'scrcpy.png', rename: 'scrcpy.png',
install_dir: join_paths(datadir, 'icons/hicolor/256x256/apps')) install_dir: datadir / 'icons/hicolor/256x256/apps')
install_data('data/zsh-completion/_scrcpy', install_data('data/zsh-completion/_scrcpy',
install_dir: join_paths(datadir, 'zsh/site-functions')) install_dir: datadir / 'zsh/site-functions')
install_data('data/bash-completion/scrcpy', install_data('data/bash-completion/scrcpy',
install_dir: join_paths(datadir, 'bash-completion/completions')) install_dir: datadir / 'bash-completion/completions')
# Desktop entry file for application launchers # Desktop entry file for application launchers
if host_machine.system() == 'linux' if host_machine.system() == 'linux'
# Install a launcher (ex: /usr/local/share/applications/scrcpy.desktop) # Install a launcher (ex: /usr/local/share/applications/scrcpy.desktop)
install_data('data/scrcpy.desktop', install_data('data/scrcpy.desktop',
install_dir: join_paths(datadir, 'applications')) install_dir: datadir / 'applications')
install_data('data/scrcpy-console.desktop', install_data('data/scrcpy-console.desktop',
install_dir: join_paths(datadir, 'applications')) install_dir: datadir / 'applications')
endif endif
@ -279,3 +279,9 @@ if get_option('buildtype') == 'debug'
test(t[0], exe) test(t[0], exe)
endforeach endforeach
endif endif
if meson.version().version_compare('>= 0.58.0')
devenv = environment()
devenv.set('SCRCPY_ICON_PATH', meson.current_source_dir() / 'data/icon.png')
meson.add_devenv(devenv)
endif

View File

@ -5,7 +5,7 @@ sc_pipe_read_intr(struct sc_intr *intr, sc_pid pid, sc_pipe pipe, char *data,
size_t len) { size_t len) {
if (intr && !sc_intr_set_process(intr, pid)) { if (intr && !sc_intr_set_process(intr, pid)) {
// Already interrupted // Already interrupted
return false; return -1;
} }
ssize_t ret = sc_pipe_read(pipe, data, len); ssize_t ret = sc_pipe_read(pipe, data, len);
@ -22,7 +22,7 @@ sc_pipe_read_all_intr(struct sc_intr *intr, sc_pid pid, sc_pipe pipe,
char *data, size_t len) { char *data, size_t len) {
if (intr && !sc_intr_set_process(intr, pid)) { if (intr && !sc_intr_set_process(intr, pid)) {
// Already interrupted // Already interrupted
return false; return -1;
} }
ssize_t ret = sc_pipe_read_all(pipe, data, len); ssize_t ret = sc_pipe_read_all(pipe, data, len);

View File

@ -7,6 +7,8 @@ cpp = 'i686-w64-mingw32-g++'
ar = 'i686-w64-mingw32-ar' ar = 'i686-w64-mingw32-ar'
strip = 'i686-w64-mingw32-strip' strip = 'i686-w64-mingw32-strip'
pkg-config = 'i686-w64-mingw32-pkg-config' pkg-config = 'i686-w64-mingw32-pkg-config'
# backward compatibility
pkgconfig = 'i686-w64-mingw32-pkg-config'
windres = 'i686-w64-mingw32-windres' windres = 'i686-w64-mingw32-windres'
[host_machine] [host_machine]

View File

@ -7,6 +7,8 @@ cpp = 'x86_64-w64-mingw32-g++'
ar = 'x86_64-w64-mingw32-ar' ar = 'x86_64-w64-mingw32-ar'
strip = 'x86_64-w64-mingw32-strip' strip = 'x86_64-w64-mingw32-strip'
pkg-config = 'x86_64-w64-mingw32-pkg-config' pkg-config = 'x86_64-w64-mingw32-pkg-config'
# backward compatibility
pkgconfig = 'x86_64-w64-mingw32-pkg-config'
windres = 'x86_64-w64-mingw32-windres' windres = 'x86_64-w64-mingw32-windres'
[host_machine] [host_machine]

View File

@ -1,6 +1,6 @@
project('scrcpy', 'c', project('scrcpy', 'c',
version: '3.1', version: '3.1',
meson_version: '>= 0.48', meson_version: '>= 0.49',
default_options: [ default_options: [
'c_std=c11', 'c_std=c11',
'warning_level=2', 'warning_level=2',

View File

@ -23,3 +23,9 @@ else
install: true, install: true,
install_dir: 'share/scrcpy') install_dir: 'share/scrcpy')
endif endif
if meson.version().version_compare('>= 0.58.0')
devenv = environment()
devenv.set('SCRCPY_SERVER_PATH', meson.current_build_dir() / 'scrcpy-server')
meson.add_devenv(devenv)
endif

View File

@ -72,7 +72,7 @@ public final class FakeContext extends ContextWrapper {
@Override @Override
public AttributionSource getAttributionSource() { public AttributionSource getAttributionSource() {
AttributionSource.Builder builder = new AttributionSource.Builder(Process.SHELL_UID); AttributionSource.Builder builder = new AttributionSource.Builder(Process.SHELL_UID);
builder.setPackageName(PACKAGE_NAME); builder.setPackageName("shell");
return builder.build(); return builder.build();
} }

View File

@ -42,6 +42,11 @@ public final class Workarounds {
Field sCurrentActivityThreadField = ACTIVITY_THREAD_CLASS.getDeclaredField("sCurrentActivityThread"); Field sCurrentActivityThreadField = ACTIVITY_THREAD_CLASS.getDeclaredField("sCurrentActivityThread");
sCurrentActivityThreadField.setAccessible(true); sCurrentActivityThreadField.setAccessible(true);
sCurrentActivityThreadField.set(null, ACTIVITY_THREAD); sCurrentActivityThreadField.set(null, ACTIVITY_THREAD);
// activityThread.mSystemThread = true;
Field mSystemThreadField = ACTIVITY_THREAD_CLASS.getDeclaredField("mSystemThread");
mSystemThreadField.setAccessible(true);
mSystemThreadField.setBoolean(ACTIVITY_THREAD, true);
} catch (Exception e) { } catch (Exception e) {
throw new AssertionError(e); throw new AssertionError(e);
} }
@ -132,10 +137,13 @@ public final class Workarounds {
try { try {
Class<?> configurationControllerClass = Class.forName("android.app.ConfigurationController"); Class<?> configurationControllerClass = Class.forName("android.app.ConfigurationController");
Class<?> activityThreadInternalClass = Class.forName("android.app.ActivityThreadInternal"); Class<?> activityThreadInternalClass = Class.forName("android.app.ActivityThreadInternal");
// configurationController = new ConfigurationController(ACTIVITY_THREAD);
Constructor<?> configurationControllerConstructor = configurationControllerClass.getDeclaredConstructor(activityThreadInternalClass); Constructor<?> configurationControllerConstructor = configurationControllerClass.getDeclaredConstructor(activityThreadInternalClass);
configurationControllerConstructor.setAccessible(true); configurationControllerConstructor.setAccessible(true);
Object configurationController = configurationControllerConstructor.newInstance(ACTIVITY_THREAD); Object configurationController = configurationControllerConstructor.newInstance(ACTIVITY_THREAD);
// ACTIVITY_THREAD.mConfigurationController = configurationController;
Field configurationControllerField = ACTIVITY_THREAD_CLASS.getDeclaredField("mConfigurationController"); Field configurationControllerField = ACTIVITY_THREAD_CLASS.getDeclaredField("mConfigurationController");
configurationControllerField.setAccessible(true); configurationControllerField.setAccessible(true);
configurationControllerField.set(ACTIVITY_THREAD, configurationController); configurationControllerField.set(ACTIVITY_THREAD, configurationController);

View File

@ -120,18 +120,40 @@ public final class LogUtils {
} }
} }
private static boolean isCameraBackwardCompatible(CameraCharacteristics characteristics) {
int[] capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
if (capabilities == null) {
return false;
}
for (int capability : capabilities) {
if (capability == CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) {
return true;
}
}
return false;
}
public static String buildCameraListMessage(boolean includeSizes) { public static String buildCameraListMessage(boolean includeSizes) {
StringBuilder builder = new StringBuilder("List of cameras:"); StringBuilder builder = new StringBuilder("List of cameras:");
CameraManager cameraManager = ServiceManager.getCameraManager(); CameraManager cameraManager = ServiceManager.getCameraManager();
try { try {
String[] cameraIds = cameraManager.getCameraIdList(); String[] cameraIds = cameraManager.getCameraIdList();
if (cameraIds == null || cameraIds.length == 0) { if (cameraIds.length == 0) {
builder.append("\n (none)"); builder.append("\n (none)");
} else { } else {
for (String id : cameraIds) { for (String id : cameraIds) {
builder.append("\n --camera-id=").append(id);
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id); CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id);
if (!isCameraBackwardCompatible(characteristics)) {
// Ignore depth cameras as suggested by official documentation
// <https://developer.android.com/media/camera/camera2/camera-enumeration>
continue;
}
builder.append("\n --camera-id=").append(id);
int facing = characteristics.get(CameraCharacteristics.LENS_FACING); int facing = characteristics.get(CameraCharacteristics.LENS_FACING);
builder.append(" (").append(getCameraFacingName(facing)).append(", "); builder.append(" (").append(getCameraFacingName(facing)).append(", ");
@ -141,8 +163,10 @@ public final class LogUtils {
try { try {
// Capture frame rates for low-FPS mode are the same for every resolution // Capture frame rates for low-FPS mode are the same for every resolution
Range<Integer>[] lowFpsRanges = characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES); Range<Integer>[] lowFpsRanges = characteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
if (lowFpsRanges != null) {
SortedSet<Integer> uniqueLowFps = getUniqueSet(lowFpsRanges); SortedSet<Integer> uniqueLowFps = getUniqueSet(lowFpsRanges);
builder.append(", fps=").append(uniqueLowFps); builder.append(", fps=").append(uniqueLowFps);
}
} catch (Exception e) { } catch (Exception e) {
// Some devices may provide invalid ranges, causing an IllegalArgumentException "lower must be less than or equal to upper" // Some devices may provide invalid ranges, causing an IllegalArgumentException "lower must be less than or equal to upper"
Ln.w("Could not get available frame rates for camera " + id, e); Ln.w("Could not get available frame rates for camera " + id, e);