mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-04-22 04:25:01 +00:00
Replace shadowed android.system with custom androidx.system package
So that we don't shadow framework classes in our binary. Update buffer position in OsCompat.write (fixed in Lollipop MR1). Synthesize InterruptedIOException in Kitkat like in Lollipop. OsCompat never throws EINTR errno.
This commit is contained in:
parent
c57ff1dc2a
commit
a82a1d837e
16 changed files with 321 additions and 86 deletions
|
@ -154,10 +154,4 @@ page at http://checkstyle.sourceforge.net/config.html -->
|
|||
<module name="UpperEll" />
|
||||
</module>
|
||||
|
||||
<!-- Excludes android.system polyfill. -->
|
||||
<!-- See https://checkstyle.org/config_filefilters.html -->
|
||||
<module name="BeforeExecutionExclusionFileFilter">
|
||||
<property name="fileNamePattern" value=".*[\\/]java[\\/]android[\\/]system[\\/].*$" />
|
||||
</module>
|
||||
|
||||
</module>
|
||||
|
|
|
@ -19,7 +19,6 @@ import java.io.FileDescriptor;
|
|||
import java.nio.ByteBuffer;
|
||||
|
||||
public interface Os {
|
||||
public String strerror(int errno);
|
||||
|
||||
public int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException;
|
||||
String strerror(int errno);
|
||||
int write(FileDescriptor fd, ByteBuffer buffer) throws ErrnoException;
|
||||
}
|
||||
|
|
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"
|
|
@ -13,30 +13,19 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.system;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
package androidx.system;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketException;
|
||||
|
||||
import libcore.io.Libcore;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@TargetApi(21)
|
||||
public final class ErrnoException extends Exception {
|
||||
private final String functionName;
|
||||
|
||||
/**
|
||||
* The errno value, for comparison with the {@code E} constants in {@link OsConstants}.
|
||||
*/
|
||||
public final int errno;
|
||||
private final int errno;
|
||||
|
||||
/**
|
||||
* Constructs an instance with the given function name and errno value.
|
||||
|
@ -55,17 +44,24 @@ public final class ErrnoException extends Exception {
|
|||
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 = OsConstants.errnoName(errno);
|
||||
String errnoName = OsConstantsCompat.errnoName(errno);
|
||||
if (errnoName == null) {
|
||||
errnoName = "errno " + errno;
|
||||
}
|
||||
String description = Libcore.os.strerror(errno);
|
||||
String description = OsCompat.strerror(errno);
|
||||
return functionName + " failed: " + errnoName + " (" + description + ")";
|
||||
}
|
||||
|
||||
|
@ -77,9 +73,7 @@ public final class ErrnoException extends Exception {
|
|||
* {@code throw e.rethrowAsIOException()} to make that clear to the compiler.
|
||||
*/
|
||||
public IOException rethrowAsIOException() throws IOException {
|
||||
IOException newException = new IOException(getMessage());
|
||||
newException.initCause(this);
|
||||
throw newException;
|
||||
throw new IOException(getMessage(), this);
|
||||
}
|
||||
|
||||
/**
|
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,41 +13,46 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.system;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
package androidx.system;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import libcore.io.Libcore;
|
||||
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 OsConstants}.
|
||||
* <p>The corresponding constants can be found in {@link OsConstantsCompat}.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
@TargetApi(21)
|
||||
public final class Os {
|
||||
private Os() {}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
try {
|
||||
return Libcore.os.write(fd, buffer);
|
||||
} catch (libcore.io.ErrnoException e) {
|
||||
throw new ErrnoException("write", e.errno);
|
||||
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);
|
||||
}
|
||||
}
|
|
@ -13,23 +13,31 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.system;
|
||||
package androidx.system;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.system.Os;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@TargetApi(21)
|
||||
public final class OsConstants {
|
||||
import static android.os.Build.VERSION.SDK_INT;
|
||||
|
||||
private OsConstants() { }
|
||||
public final class OsConstantsCompat {
|
||||
private OsConstantsCompat() {
|
||||
}
|
||||
|
||||
public static final int EINTR = libcore.io.OsConstants.EINTR;
|
||||
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 libcore.io.OsConstants.errnoName(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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,7 +22,7 @@ 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")
|
||||
compileOnly project(':libcore')
|
||||
implementation project(':os-compat')
|
||||
}
|
||||
|
||||
apply from: "$project.rootDir/config/android-checkstyle.gradle"
|
||||
|
|
|
@ -52,10 +52,12 @@ echo "Compiling java sources..."
|
|||
cd ../java
|
||||
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 \
|
||||
android/system/*.java \
|
||||
$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
|
||||
|
@ -68,7 +70,7 @@ then
|
|||
# use dx
|
||||
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/dx" --dex \
|
||||
--output "$BUILD_DIR/classes.dex" \
|
||||
android/system/*.class \
|
||||
androidx/system/*.class \
|
||||
android/view/*.class \
|
||||
android/content/*.class \
|
||||
com/genymobile/scrcpy/*.class \
|
||||
|
@ -82,7 +84,7 @@ else
|
|||
# use d8
|
||||
"$ANDROID_HOME/build-tools/$BUILD_TOOLS/d8" --classpath "$ANDROID_JAR" \
|
||||
--output "$BUILD_DIR/classes.zip" \
|
||||
android/system/*.class \
|
||||
androidx/system/*.class \
|
||||
android/view/*.class \
|
||||
android/content/*.class \
|
||||
com/genymobile/scrcpy/*.class \
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
package com.genymobile.scrcpy;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
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,25 +12,13 @@ public final class IO {
|
|||
// not instantiable
|
||||
}
|
||||
|
||||
@TargetApi(21) // Backported subset of android.system.
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
include ':server'
|
||||
include ':os-compat'
|
||||
include ':libcore'
|
||||
|
|
Loading…
Add table
Reference in a new issue