mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-04-22 04:25:01 +00:00
Merge d535bc980d
into c00a9ead5e
This commit is contained in:
commit
8be9ce0aae
27 changed files with 789 additions and 41 deletions
|
@ -38,7 +38,7 @@ Its features include:
|
|||
|
||||
## Requirements
|
||||
|
||||
The Android device requires at least API 21 (Android 5.0).
|
||||
The Android device requires at least API 19 (Android 4.4).
|
||||
|
||||
Make sure you [enable adb debugging][enable-adb] on your device(s).
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#define SC_SERVER_PATH_DEFAULT PREFIX "/share/scrcpy/" SC_SERVER_FILENAME
|
||||
#define SC_DEVICE_SERVER_PATH "/data/local/tmp/scrcpy-server.jar"
|
||||
#define SC_DEVICE_ANDROID_DATA_PATH "/data/local/tmp"
|
||||
|
||||
static char *
|
||||
get_server_path(void) {
|
||||
|
@ -166,6 +167,9 @@ execute_server(struct sc_server *server,
|
|||
cmd[count++] = "-s";
|
||||
cmd[count++] = serial;
|
||||
cmd[count++] = "shell";
|
||||
cmd[count++] = "mkdir -p " SC_DEVICE_ANDROID_DATA_PATH "/dalvik-cache";
|
||||
cmd[count++] = "&&";
|
||||
cmd[count++] = "ANDROID_DATA=" SC_DEVICE_ANDROID_DATA_PATH;
|
||||
cmd[count++] = "CLASSPATH=" SC_DEVICE_SERVER_PATH;
|
||||
cmd[count++] = "app_process";
|
||||
|
||||
|
|
|
@ -152,7 +152,6 @@ page at http://checkstyle.sourceforge.net/config.html -->
|
|||
<property name="severity" value="info" />
|
||||
</module>
|
||||
<module name="UpperEll" />
|
||||
|
||||
</module>
|
||||
|
||||
</module>
|
||||
|
|
|
@ -16,3 +16,4 @@ org.gradle.jvmargs=-Xmx1536m
|
|||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
android.useAndroidX=true
|
||||
|
|
1
libcore/build.gradle
Normal file
1
libcore/build.gradle
Normal file
|
@ -0,0 +1 @@
|
|||
apply plugin: 'java-library'
|
30
libcore/src/main/java/libcore/io/ErrnoException.java
Normal file
30
libcore/src/main/java/libcore/io/ErrnoException.java
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package libcore.io;
|
||||
|
||||
/**
|
||||
* A checked exception thrown when {@link Os} methods fail. This exception contains the native
|
||||
* errno value, for comparison against the constants in {@link OsConstants}, should sophisticated
|
||||
* callers need to adjust their behavior based on the exact failure.
|
||||
*/
|
||||
public final class ErrnoException extends Exception {
|
||||
private final String functionName;
|
||||
public final int errno;
|
||||
|
||||
public ErrnoException(String functionName, int errno) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
23
libcore/src/main/java/libcore/io/Libcore.java
Normal file
23
libcore/src/main/java/libcore/io/Libcore.java
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package libcore.io;
|
||||
|
||||
public final class Libcore {
|
||||
private Libcore() {
|
||||
}
|
||||
|
||||
public static Os os = null;
|
||||
}
|
24
libcore/src/main/java/libcore/io/Os.java
Normal file
24
libcore/src/main/java/libcore/io/Os.java
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package libcore.io;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public interface Os {
|
||||
String strerror(int errno);
|
||||
int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException;
|
||||
}
|
30
libcore/src/main/java/libcore/io/OsConstants.java
Normal file
30
libcore/src/main/java/libcore/io/OsConstants.java
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package libcore.io;
|
||||
|
||||
public final class OsConstants {
|
||||
private OsConstants() {
|
||||
}
|
||||
|
||||
public static final int EINTR = placeholder();
|
||||
|
||||
public static String errnoName(int errno) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// A hack to avoid these constants being inlined by javac...
|
||||
private static int placeholder() { return 0; }
|
||||
}
|
24
os-compat/build.gradle
Normal file
24
os-compat/build.gradle
Normal file
|
@ -0,0 +1,24 @@
|
|||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 31
|
||||
defaultConfig {
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 31
|
||||
}
|
||||
androidResources {
|
||||
namespace = "androidx.system"
|
||||
}
|
||||
buildFeatures {
|
||||
aidl = false
|
||||
androidResources = false
|
||||
buildConfig = false
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly rootProject.fileTree("thirdparty/androidx/annotation/1.3.0/annotation-1.3.0.jar")
|
||||
compileOnly project(':libcore')
|
||||
}
|
||||
|
||||
apply from: "$project.rootDir/config/android-checkstyle.gradle"
|
91
os-compat/src/main/java/androidx/system/ErrnoException.java
Normal file
91
os-compat/src/main/java/androidx/system/ErrnoException.java
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.system;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketException;
|
||||
|
||||
/**
|
||||
* A checked exception thrown when {@link OsCompat} methods fail. This exception contains the native
|
||||
* errno value, for comparison against the constants in {@link OsConstantsCompat}, should sophisticated
|
||||
* callers need to adjust their behavior based on the exact failure.
|
||||
*/
|
||||
public final class ErrnoException extends Exception {
|
||||
private final String functionName;
|
||||
private final int errno;
|
||||
|
||||
/**
|
||||
* Constructs an instance with the given function name and errno value.
|
||||
*/
|
||||
public ErrnoException(String functionName, int errno) {
|
||||
this.functionName = functionName;
|
||||
this.errno = errno;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance with the given function name, errno value, and cause.
|
||||
*/
|
||||
public ErrnoException(String functionName, int errno, Throwable cause) {
|
||||
super(cause);
|
||||
this.functionName = functionName;
|
||||
this.errno = errno;
|
||||
}
|
||||
|
||||
/**
|
||||
* The errno value, for comparison with the {@code E} constants in {@link OsConstantsCompat}.
|
||||
*/
|
||||
public int getErrno() {
|
||||
return errno;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the stashed function name and errno value to a human-readable string.
|
||||
* We do this here rather than in the constructor so that callers only pay for
|
||||
* this if they need it.
|
||||
*/
|
||||
@Override public String getMessage() {
|
||||
String errnoName = OsConstantsCompat.errnoName(errno);
|
||||
if (errnoName == null) {
|
||||
errnoName = "errno " + errno;
|
||||
}
|
||||
String description = OsCompat.strerror(errno);
|
||||
return functionName + " failed: " + errnoName + " (" + description + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an {@link IOException} with a message based on {@link #getMessage()} and with this
|
||||
* instance as the cause.
|
||||
*
|
||||
* <p>This method always terminates by throwing the exception. Callers can write
|
||||
* {@code throw e.rethrowAsIOException()} to make that clear to the compiler.
|
||||
*/
|
||||
public IOException rethrowAsIOException() throws IOException {
|
||||
throw new IOException(getMessage(), this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws a {@link SocketException} with a message based on {@link #getMessage()} and with this
|
||||
* instance as the cause.
|
||||
*
|
||||
* <p>This method always terminates by throwing the exception. Callers can write
|
||||
* {@code throw e.rethrowAsIOException()} to make that clear to the compiler.
|
||||
*/
|
||||
public SocketException rethrowAsSocketException() throws SocketException {
|
||||
final SocketException newException = new SocketException(getMessage());
|
||||
newException.initCause(this);
|
||||
throw newException;
|
||||
}
|
||||
}
|
33
os-compat/src/main/java/androidx/system/Os.java
Normal file
33
os-compat/src/main/java/androidx/system/Os.java
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.system;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
interface Os {
|
||||
String strerror(int errno);
|
||||
|
||||
int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException;
|
||||
|
||||
// https://android.googlesource.com/platform/libcore/+/lollipop-mr1-release/luni/src/main/java/libcore/io/Posix.java#253
|
||||
static void maybeUpdateBufferPosition(ByteBuffer buffer, int originalPosition, int bytesReadOrWritten) {
|
||||
if (bytesReadOrWritten > 0) {
|
||||
buffer.position(bytesReadOrWritten + originalPosition);
|
||||
}
|
||||
}
|
||||
}
|
47
os-compat/src/main/java/androidx/system/OsApi21.java
Normal file
47
os-compat/src/main/java/androidx/system/OsApi21.java
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.system;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
import static androidx.system.Os.maybeUpdateBufferPosition;
|
||||
|
||||
@RequiresApi(21)
|
||||
final class OsApi21 implements Os {
|
||||
@Override
|
||||
public String strerror(int errno) {
|
||||
return android.system.Os.strerror(errno);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
|
||||
try {
|
||||
final int position = buffer.position();
|
||||
final int bytesWritten = android.system.Os.write(fd, buffer);
|
||||
if (SDK_INT < 22) {
|
||||
maybeUpdateBufferPosition(buffer, position, bytesWritten);
|
||||
}
|
||||
return bytesWritten;
|
||||
} catch (android.system.ErrnoException e) {
|
||||
throw new ErrnoException("write", e.errno);
|
||||
}
|
||||
}
|
||||
}
|
58
os-compat/src/main/java/androidx/system/OsCompat.java
Normal file
58
os-compat/src/main/java/androidx/system/OsCompat.java
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.system;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
|
||||
/**
|
||||
* Access to low-level system functionality. Most of these are system calls. Most users will want
|
||||
* to use higher-level APIs where available, but this class provides access to the underlying
|
||||
* primitives used to implement the higher-level APIs.
|
||||
*
|
||||
* <p>The corresponding constants can be found in {@link OsConstantsCompat}.
|
||||
*/
|
||||
public final class OsCompat {
|
||||
private OsCompat() {
|
||||
}
|
||||
|
||||
private static final Os IMPL;
|
||||
|
||||
static {
|
||||
if (SDK_INT >= 21) {
|
||||
IMPL = new OsApi21();
|
||||
} else {
|
||||
IMPL = new OsLibcore();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* See <a href="http://man7.org/linux/man-pages/man3/strerror.3.html">strerror(2)</a>.
|
||||
*/
|
||||
public static String strerror(int errno) {
|
||||
return IMPL.strerror(errno);
|
||||
}
|
||||
|
||||
/**
|
||||
* See <a href="http://man7.org/linux/man-pages/man2/write.2.html">write(2)</a>.
|
||||
*/
|
||||
public static int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
|
||||
return IMPL.write(fd, buffer);
|
||||
}
|
||||
}
|
21
os-compat/src/main/java/androidx/system/OsConstants.java
Normal file
21
os-compat/src/main/java/androidx/system/OsConstants.java
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.system;
|
||||
|
||||
interface OsConstants {
|
||||
|
||||
String errnoName(int errno);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package androidx.system;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
@RequiresApi(21)
|
||||
final class OsConstantsApi21 implements OsConstants {
|
||||
@Override
|
||||
public String errnoName(int errno) {
|
||||
return android.system.OsConstants.errnoName(errno);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.system;
|
||||
|
||||
import android.system.Os;
|
||||
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
|
||||
public final class OsConstantsCompat {
|
||||
private OsConstantsCompat() {
|
||||
}
|
||||
|
||||
private static final OsConstants IMPL;
|
||||
|
||||
static {
|
||||
if (SDK_INT >= 21) {
|
||||
IMPL = new OsConstantsApi21();
|
||||
} else {
|
||||
IMPL = new OsConstantsLibcore();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string name of an errno value.
|
||||
* For example, "EACCES". See {@link Os#strerror} for human-readable errno descriptions.
|
||||
*/
|
||||
public static String errnoName(int errno) {
|
||||
return IMPL.errnoName(errno);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package androidx.system;
|
||||
|
||||
final class OsConstantsLibcore implements OsConstants {
|
||||
@Override
|
||||
public String errnoName(int errno) {
|
||||
return libcore.io.OsConstants.errnoName(errno);
|
||||
}
|
||||
}
|
102
os-compat/src/main/java/androidx/system/OsLibcore.java
Normal file
102
os-compat/src/main/java/androidx/system/OsLibcore.java
Normal file
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (C) 2011 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package androidx.system;
|
||||
|
||||
import libcore.io.Libcore;
|
||||
import libcore.io.OsConstants;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static androidx.system.Os.maybeUpdateBufferPosition;
|
||||
|
||||
final class OsLibcore implements Os {
|
||||
@Override
|
||||
public String strerror(int errno) {
|
||||
return Libcore.os.strerror(errno);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException, InterruptedIOException {
|
||||
try {
|
||||
return ioFailureRetry("write", fd, () -> {
|
||||
final int position = buffer.position();
|
||||
final int bytesWritten = Libcore.os.write(fd, buffer);
|
||||
maybeUpdateBufferPosition(buffer, position, bytesWritten);
|
||||
return bytesWritten;
|
||||
});
|
||||
} catch (libcore.io.ErrnoException e) {
|
||||
throw new ErrnoException("write", e.errno);
|
||||
}
|
||||
}
|
||||
|
||||
// https://android.googlesource.com/platform/libcore/+/lollipop-release/luni/src/main/native/libcore_io_Posix.cpp#128
|
||||
// https://android.googlesource.com/platform/libcore/+/kitkat-release/luni/src/main/java/libcore/io/IoBridge.java#186
|
||||
private static int ioFailureRetry(String functionName, FileDescriptor fd, SysCall syscall)
|
||||
throws libcore.io.ErrnoException, InterruptedIOException {
|
||||
if (!fd.valid()) {
|
||||
throw UndeclaredExceptions.raise(new IOException("File descriptor closed"));
|
||||
}
|
||||
int rc = -1;
|
||||
do {
|
||||
int syscallErrno = 0;
|
||||
try {
|
||||
rc = syscall.call();
|
||||
} catch (libcore.io.ErrnoException e) {
|
||||
syscallErrno = e.errno;
|
||||
}
|
||||
if (rc == -1 && !fd.valid()) {
|
||||
throw new InterruptedIOException(functionName + " interrupted");
|
||||
}
|
||||
if (rc == -1 && syscallErrno != OsConstants.EINTR) {
|
||||
throw new libcore.io.ErrnoException(functionName, syscallErrno);
|
||||
}
|
||||
} while (rc == -1);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface SysCall {
|
||||
|
||||
Integer call() throws libcore.io.ErrnoException;
|
||||
}
|
||||
|
||||
// https://dzone.com/articles/throwing-undeclared-checked
|
||||
private static final class UndeclaredExceptions extends RuntimeException {
|
||||
private static Throwable sThrowable = null;
|
||||
|
||||
public static synchronized RuntimeException raise(Throwable throwable) {
|
||||
if (throwable instanceof ReflectiveOperationException || throwable instanceof RuntimeException) {
|
||||
throw new IllegalArgumentException("Unsupported exception: " + throwable.getClass());
|
||||
}
|
||||
|
||||
sThrowable = throwable;
|
||||
try {
|
||||
return UndeclaredExceptions.class.newInstance();
|
||||
} catch (ReflectiveOperationException e) {
|
||||
return new RuntimeException(e);
|
||||
} finally {
|
||||
sThrowable = null;
|
||||
}
|
||||
}
|
||||
|
||||
private UndeclaredExceptions() throws Throwable {
|
||||
throw sThrowable;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ android {
|
|||
compileSdkVersion 31
|
||||
defaultConfig {
|
||||
applicationId "com.genymobile.scrcpy"
|
||||
minSdkVersion 21
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 31
|
||||
versionCode 12400
|
||||
versionName "1.24"
|
||||
|
@ -21,6 +21,8 @@ android {
|
|||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
testImplementation 'junit:junit:4.13.1'
|
||||
compileOnly rootProject.fileTree("thirdparty/androidx/annotation/1.3.0/annotation-1.3.0.jar")
|
||||
implementation project(':os-compat')
|
||||
}
|
||||
|
||||
apply from: "$project.rootDir/config/android-checkstyle.gradle"
|
||||
|
|
|
@ -20,12 +20,14 @@ BUILD_TOOLS=${ANDROID_BUILD_TOOLS:-31.0.0}
|
|||
BUILD_DIR="$(realpath ${BUILD_DIR:-build_manual})"
|
||||
CLASSES_DIR="$BUILD_DIR/classes"
|
||||
SERVER_DIR=$(dirname "$0")
|
||||
ROOT_PROJECT_DIR=$(realpath $SERVER_DIR/..)
|
||||
SERVER_BINARY=scrcpy-server
|
||||
ANDROID_JAR="$ANDROID_HOME/platforms/android-$PLATFORM/android.jar"
|
||||
|
||||
echo "Platform: android-$PLATFORM"
|
||||
echo "Build-tools: $BUILD_TOOLS"
|
||||
echo "Build dir: $BUILD_DIR"
|
||||
echo "Root project dir: $ROOT_PROJECT_DIR"
|
||||
|
||||
rm -rf "$CLASSES_DIR" "$BUILD_DIR/$SERVER_BINARY" classes.dex
|
||||
mkdir -p "$CLASSES_DIR/com/genymobile/scrcpy"
|
||||
|
@ -48,8 +50,15 @@ cd "$SERVER_DIR/src/main/aidl"
|
|||
|
||||
echo "Compiling java sources..."
|
||||
cd ../java
|
||||
javac -bootclasspath "$ANDROID_JAR" -cp "$CLASSES_DIR" -d "$CLASSES_DIR" \
|
||||
classpath="$CLASSES_DIR"
|
||||
classpath="$classpath:$ROOT_PROJECT_DIR/thirdparty/androidx/annotation/1.3.0/annotation-1.3.0.jar"
|
||||
# https://stackoverflow.com/a/58768648/2444099
|
||||
classpath="$classpath:$ANDROID_HOME/build-tools/$BUILD_TOOLS/core-lambda-stubs.jar"
|
||||
javac -bootclasspath "$ANDROID_JAR" -cp "$classpath" -d "$CLASSES_DIR" \
|
||||
-encoding UTF-8 \
|
||||
-source 1.8 -target 1.8 \
|
||||
$ROOT_PROJECT_DIR/os-compat/src/main/java/androidx/system/*.java \
|
||||
$ROOT_PROJECT_DIR/libcore/src/main/java/libcore/io/*.java \
|
||||
com/genymobile/scrcpy/*.java \
|
||||
com/genymobile/scrcpy/wrappers/*.java
|
||||
|
||||
|
@ -61,6 +70,7 @@ then
|
|||
# use dx
|
||||
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/dx" --dex \
|
||||
--output "$BUILD_DIR/classes.dex" \
|
||||
androidx/system/*.class \
|
||||
android/view/*.class \
|
||||
android/content/*.class \
|
||||
com/genymobile/scrcpy/*.class \
|
||||
|
@ -74,6 +84,7 @@ else
|
|||
# use d8
|
||||
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/d8" --classpath "$ANDROID_JAR" \
|
||||
--output "$BUILD_DIR/classes.zip" \
|
||||
androidx/system/*.class \
|
||||
android/view/*.class \
|
||||
android/content/*.class \
|
||||
com/genymobile/scrcpy/*.class \
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package com.genymobile.scrcpy;
|
||||
|
||||
import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.system.OsConstants;
|
||||
import androidx.system.ErrnoException;
|
||||
import androidx.system.OsCompat;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
|
@ -14,23 +13,12 @@ public final class IO {
|
|||
}
|
||||
|
||||
public static void writeFully(FileDescriptor fd, ByteBuffer from) throws IOException {
|
||||
// ByteBuffer position is not updated as expected by Os.write() on old Android versions, so
|
||||
// count the remaining bytes manually.
|
||||
// See <https://github.com/Genymobile/scrcpy/issues/291>.
|
||||
int remaining = from.remaining();
|
||||
while (remaining > 0) {
|
||||
try {
|
||||
int w = Os.write(fd, from);
|
||||
if (BuildConfig.DEBUG && w < 0) {
|
||||
// w should not be negative, since an exception is thrown on error
|
||||
throw new AssertionError("Os.write() returned a negative value (" + w + ")");
|
||||
}
|
||||
remaining -= w;
|
||||
} catch (ErrnoException e) {
|
||||
if (e.errno != OsConstants.EINTR) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
try {
|
||||
while (from.hasRemaining()) {
|
||||
OsCompat.write(fd, from);
|
||||
}
|
||||
} catch (ErrnoException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
116
server/src/main/java/com/genymobile/scrcpy/MediaCodecCompat.java
Normal file
116
server/src/main/java/com/genymobile/scrcpy/MediaCodecCompat.java
Normal file
|
@ -0,0 +1,116 @@
|
|||
package com.genymobile.scrcpy;
|
||||
|
||||
import android.media.MediaCodec;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static android.media.MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
|
||||
/**
|
||||
* Version of {@link MediaCodec} that backports {@link #getOutputBuffer} to Kitkat.
|
||||
* The backported implementation isn't thread safe.
|
||||
*/
|
||||
abstract class MediaCodecCompat {
|
||||
private final MediaCodec delegate;
|
||||
|
||||
MediaCodecCompat(@NonNull MediaCodec delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
protected MediaCodec getDelegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static MediaCodecCompat wrap(@NonNull MediaCodec codec) {
|
||||
if (SDK_INT >= 21) {
|
||||
return new Platform(codec);
|
||||
} else {
|
||||
return new Backport(codec);
|
||||
}
|
||||
}
|
||||
|
||||
abstract int dequeueOutputBuffer(
|
||||
@NonNull MediaCodec.BufferInfo info, long timeoutUs);
|
||||
|
||||
@Nullable
|
||||
abstract ByteBuffer getOutputBuffer(int index);
|
||||
|
||||
abstract void releaseOutputBuffer(int index, boolean render);
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static class Backport extends MediaCodecCompat {
|
||||
private ByteBuffer[] cachedOutputBuffers = null;
|
||||
|
||||
Backport(@NonNull MediaCodec delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
int dequeueOutputBuffer(
|
||||
@NonNull MediaCodec.BufferInfo info, long timeoutUs) {
|
||||
final int res = getDelegate().dequeueOutputBuffer(info, timeoutUs);
|
||||
if (res == INFO_OUTPUT_BUFFERS_CHANGED) {
|
||||
cachedOutputBuffers = null;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
ByteBuffer getOutputBuffer(int index) {
|
||||
if (cachedOutputBuffers == null) {
|
||||
cacheOutputBuffers();
|
||||
}
|
||||
if (cachedOutputBuffers == null) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return cachedOutputBuffers[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
void releaseOutputBuffer(int index, boolean render) {
|
||||
cachedOutputBuffers = null;
|
||||
getDelegate().releaseOutputBuffer(index, render);
|
||||
}
|
||||
|
||||
private void cacheOutputBuffers() {
|
||||
ByteBuffer[] buffers = null;
|
||||
try {
|
||||
buffers = getDelegate().getOutputBuffers();
|
||||
} catch (IllegalStateException e) {
|
||||
// we don't get buffers in async mode
|
||||
}
|
||||
cachedOutputBuffers = buffers;
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
private static class Platform extends MediaCodecCompat {
|
||||
Platform(@NonNull MediaCodec delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
@Override
|
||||
int dequeueOutputBuffer(
|
||||
@NonNull MediaCodec.BufferInfo info, long timeoutUs) {
|
||||
return getDelegate().dequeueOutputBuffer(info, timeoutUs);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
ByteBuffer getOutputBuffer(int index) {
|
||||
return getDelegate().getOutputBuffer(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
void releaseOutputBuffer(int index, boolean render) {
|
||||
getDelegate().releaseOutputBuffer(index, render);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package com.genymobile.scrcpy;
|
||||
|
||||
import android.media.MediaCodecInfo;
|
||||
import android.media.MediaCodecList;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static android.media.MediaCodecList.REGULAR_CODECS;
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
|
||||
/**
|
||||
* Version of {@link MediaCodecList} that backports {@link #getCodecInfos()} to Kitkat.
|
||||
*/
|
||||
abstract class MediaCodecListCompat {
|
||||
MediaCodecListCompat() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of codecs that are suitable
|
||||
* for regular (buffer-to-buffer) decoding or encoding.
|
||||
*/
|
||||
static MediaCodecListCompat regular() {
|
||||
if (SDK_INT >= 21) {
|
||||
return new Platform();
|
||||
} else {
|
||||
return new Backport();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of {@link MediaCodecInfo} objects for the list
|
||||
* of media-codecs.
|
||||
*/
|
||||
@NonNull
|
||||
abstract MediaCodecInfo[] getCodecInfos();
|
||||
|
||||
@NonNull
|
||||
final MediaCodecInfo[] getEncoderInfosForType(@NonNull String mimeType) {
|
||||
List<MediaCodecInfo> result = new ArrayList<>();
|
||||
MediaCodecInfo[] codecInfos = MediaCodecListCompat.regular().getCodecInfos();
|
||||
for (MediaCodecInfo codecInfo : codecInfos) {
|
||||
if (codecInfo.isEncoder() && Arrays.asList(codecInfo.getSupportedTypes()).contains(mimeType)) {
|
||||
result.add(codecInfo);
|
||||
}
|
||||
}
|
||||
return result.toArray(new MediaCodecInfo[result.size()]);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static class Backport extends MediaCodecListCompat {
|
||||
private final MediaCodecInfo[] mCodecInfos;
|
||||
|
||||
Backport() {
|
||||
final int size = MediaCodecList.getCodecCount();
|
||||
final MediaCodecInfo[] codecInfos = new MediaCodecInfo[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
codecInfos[i] = MediaCodecList.getCodecInfoAt(i);
|
||||
}
|
||||
this.mCodecInfos = codecInfos;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public final MediaCodecInfo[] getCodecInfos() {
|
||||
return Arrays.copyOf(mCodecInfos, mCodecInfos.length);
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
private static class Platform extends MediaCodecListCompat {
|
||||
private final MediaCodecList mDelegate;
|
||||
|
||||
Platform() {
|
||||
this.mDelegate = new MediaCodecList(REGULAR_CODECS);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
MediaCodecInfo[] getCodecInfos() {
|
||||
return mDelegate.getCodecInfos();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,10 +2,10 @@ package com.genymobile.scrcpy;
|
|||
|
||||
import com.genymobile.scrcpy.wrappers.SurfaceControl;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Rect;
|
||||
import android.media.MediaCodec;
|
||||
import android.media.MediaCodecInfo;
|
||||
import android.media.MediaCodecList;
|
||||
import android.media.MediaFormat;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
|
@ -14,8 +14,6 @@ import android.view.Surface;
|
|||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
|
@ -25,6 +23,9 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||
private static final int REPEAT_FRAME_DELAY_US = 100_000; // repeat after 100ms
|
||||
private static final String KEY_MAX_FPS_TO_ENCODER = "max-fps-to-encoder";
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
public static final String MIMETYPE_VIDEO_AVC = MediaFormat.MIMETYPE_VIDEO_AVC;
|
||||
|
||||
// Keep the values in descending order
|
||||
private static final int[] MAX_SIZE_FALLBACK = {2560, 1920, 1600, 1280, 1024, 800};
|
||||
|
||||
|
@ -144,7 +145,8 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||
return 0;
|
||||
}
|
||||
|
||||
private boolean encode(MediaCodec codec, FileDescriptor fd) throws IOException {
|
||||
private boolean encode(MediaCodec platformCodec, FileDescriptor fd) throws IOException {
|
||||
final MediaCodecCompat codec = MediaCodecCompat.wrap(platformCodec);
|
||||
boolean eof = false;
|
||||
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
||||
|
||||
|
@ -201,28 +203,18 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||
IO.writeFully(fd, headerBuffer);
|
||||
}
|
||||
|
||||
private static MediaCodecInfo[] listEncoders() {
|
||||
List<MediaCodecInfo> result = new ArrayList<>();
|
||||
MediaCodecList list = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
|
||||
for (MediaCodecInfo codecInfo : list.getCodecInfos()) {
|
||||
if (codecInfo.isEncoder() && Arrays.asList(codecInfo.getSupportedTypes()).contains(MediaFormat.MIMETYPE_VIDEO_AVC)) {
|
||||
result.add(codecInfo);
|
||||
}
|
||||
}
|
||||
return result.toArray(new MediaCodecInfo[result.size()]);
|
||||
}
|
||||
|
||||
private static MediaCodec createCodec(String encoderName) throws IOException {
|
||||
if (encoderName != null) {
|
||||
Ln.d("Creating encoder by name: '" + encoderName + "'");
|
||||
try {
|
||||
return MediaCodec.createByCodecName(encoderName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
MediaCodecInfo[] encoders = listEncoders();
|
||||
MediaCodecInfo[] encoders = MediaCodecListCompat.regular()
|
||||
.getEncoderInfosForType(MIMETYPE_VIDEO_AVC);
|
||||
throw new InvalidEncoderException(encoderName, encoders);
|
||||
}
|
||||
}
|
||||
MediaCodec codec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
|
||||
MediaCodec codec = MediaCodec.createEncoderByType(MIMETYPE_VIDEO_AVC);
|
||||
Ln.d("Using encoder: '" + codec.getName() + "'");
|
||||
return codec;
|
||||
}
|
||||
|
@ -246,7 +238,7 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||
|
||||
private static MediaFormat createFormat(int bitRate, int maxFps, List<CodecOption> codecOptions) {
|
||||
MediaFormat format = new MediaFormat();
|
||||
format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_VIDEO_AVC);
|
||||
format.setString(MediaFormat.KEY_MIME, MIMETYPE_VIDEO_AVC);
|
||||
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
|
||||
// must be present to configure the encoder, but does not impact the actual frame rate, which is variable
|
||||
format.setInteger(MediaFormat.KEY_FRAME_RATE, 60);
|
||||
|
|
|
@ -1 +1,3 @@
|
|||
include ':server'
|
||||
include ':os-compat'
|
||||
include ':libcore'
|
||||
|
|
BIN
thirdparty/androidx/annotation/1.3.0/annotation-1.3.0.jar
vendored
Normal file
BIN
thirdparty/androidx/annotation/1.3.0/annotation-1.3.0.jar
vendored
Normal file
Binary file not shown.
Loading…
Add table
Reference in a new issue