diff --git a/config/android-checkstyle.gradle b/config/android-checkstyle.gradle new file mode 100644 index 00000000..f998530e --- /dev/null +++ b/config/android-checkstyle.gradle @@ -0,0 +1,28 @@ +apply plugin: 'checkstyle' +check.dependsOn 'checkstyle' + +checkstyle { + toolVersion = '6.19' +} + +task checkstyle(type: Checkstyle) { + description = "Check Java style with Checkstyle" + configFile = rootProject.file("config/checkstyle/checkstyle.xml") + source = javaSources() + classpath = files() + ignoreFailures = true +} + +def javaSources() { + def files = [] + android.sourceSets.each { sourceSet -> + sourceSet.java.each { javaSource -> + javaSource.getSrcDirs().each { + if (it.exists()) { + files.add(it) + } + } + } + } + return files +} diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 00000000..63ee315a --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/server/build.gradle b/server/build.gradle index c4ae50a1..0f3a6a9c 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -1,11 +1,11 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 26 + compileSdkVersion 27 defaultConfig { applicationId "com.genymobile.scrcpy" minSdkVersion 21 - targetSdkVersion 26 + targetSdkVersion 27 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -22,3 +22,5 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) testImplementation 'junit:junit:4.12' } + +apply from: "$project.rootDir/config/android-checkstyle.gradle" diff --git a/server/src/main/java/com/genymobile/scrcpy/ControlEvent.java b/server/src/main/java/com/genymobile/scrcpy/ControlEvent.java index 44d7cdd6..43b8f930 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ControlEvent.java +++ b/server/src/main/java/com/genymobile/scrcpy/ControlEvent.java @@ -3,7 +3,7 @@ package com.genymobile.scrcpy; /** * Union of all supported event types, identified by their {@code type}. */ -public class ControlEvent { +public final class ControlEvent { public static final int TYPE_KEYCODE = 0; public static final int TYPE_TEXT = 1; diff --git a/server/src/main/java/com/genymobile/scrcpy/ControlEventReader.java b/server/src/main/java/com/genymobile/scrcpy/ControlEventReader.java index 01a76d93..4d412f9b 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ControlEventReader.java +++ b/server/src/main/java/com/genymobile/scrcpy/ControlEventReader.java @@ -12,9 +12,12 @@ public class ControlEventReader { private static final int SCROLL_PAYLOAD_LENGTH = 16; private static final int COMMAND_PAYLOAD_LENGTH = 1; - private final byte[] rawBuffer = new byte[128]; + private static final int MAX_TEXT_LENGTH = 32; + private static final int RAW_BUFFER_SIZE = 128; + + private final byte[] rawBuffer = new byte[RAW_BUFFER_SIZE]; private final ByteBuffer buffer = ByteBuffer.wrap(rawBuffer); - private final byte[] textBuffer = new byte[32]; + private final byte[] textBuffer = new byte[MAX_TEXT_LENGTH]; public ControlEventReader() { // invariant: the buffer is always in "get" mode @@ -136,10 +139,12 @@ public class ControlEventReader { return new Position(x, y, screenWidth, screenHeight); } + @SuppressWarnings("checkstyle:MagicNumber") private static int toUnsigned(short value) { return value & 0xffff; } + @SuppressWarnings("checkstyle:MagicNumber") private static int toUnsigned(byte value) { return value & 0xff; } diff --git a/server/src/main/java/com/genymobile/scrcpy/DesktopConnection.java b/server/src/main/java/com/genymobile/scrcpy/DesktopConnection.java index e739abe6..203ecc9b 100644 --- a/server/src/main/java/com/genymobile/scrcpy/DesktopConnection.java +++ b/server/src/main/java/com/genymobile/scrcpy/DesktopConnection.java @@ -9,7 +9,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; -public class DesktopConnection implements Closeable { +public final class DesktopConnection implements Closeable { private static final int DEVICE_NAME_FIELD_LENGTH = 64; @@ -48,9 +48,8 @@ public class DesktopConnection implements Closeable { socket.close(); } + @SuppressWarnings("checkstyle:MagicNumber") private void send(String deviceName, int width, int height) throws IOException { - assert width < 0x10000 : "width may not be stored on 16 bits"; - assert height < 0x10000 : "height may not be stored on 16 bits"; byte[] buffer = new byte[DEVICE_NAME_FIELD_LENGTH + 4]; byte[] deviceNameBytes = deviceName.getBytes(StandardCharsets.UTF_8); diff --git a/server/src/main/java/com/genymobile/scrcpy/Device.java b/server/src/main/java/com/genymobile/scrcpy/Device.java index c6860a10..d78ea1b3 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Device.java +++ b/server/src/main/java/com/genymobile/scrcpy/Device.java @@ -1,13 +1,13 @@ package com.genymobile.scrcpy; +import com.genymobile.scrcpy.wrappers.ServiceManager; + import android.graphics.Point; import android.os.Build; import android.os.RemoteException; import android.view.IRotationWatcher; import android.view.InputEvent; -import com.genymobile.scrcpy.wrappers.ServiceManager; - public final class Device { public interface RotationListener { @@ -40,6 +40,7 @@ public final class Device { return screenInfo; } + @SuppressWarnings("checkstyle:MagicNumber") private ScreenInfo computeScreenInfo(int maxSize) { // Compute the video size and the padding of the content inside this video. // Principle: @@ -52,7 +53,9 @@ public final class Device { int w = deviceSize.getWidth(); int h = deviceSize.getHeight(); if (maxSize > 0) { - assert maxSize % 8 == 0; + if (BuildConfig.DEBUG && maxSize % 8 != 0) { + throw new AssertionError("Max size must be a multiple of 8"); + } boolean portrait = h > w; int major = portrait ? h : w; int minor = portrait ? w : h; @@ -70,6 +73,7 @@ public final class Device { } public Point getPhysicalPoint(Position position) { + @SuppressWarnings("checkstyle:HiddenField") // it hides the field on purpose, to read it with a lock ScreenInfo screenInfo = getScreenInfo(); // read with synchronization Size videoSize = screenInfo.getVideoSize(); Size clientVideoSize = position.getScreenSize(); diff --git a/server/src/main/java/com/genymobile/scrcpy/EventController.java b/server/src/main/java/com/genymobile/scrcpy/EventController.java index 6d2ec3dd..0af1aed8 100644 --- a/server/src/main/java/com/genymobile/scrcpy/EventController.java +++ b/server/src/main/java/com/genymobile/scrcpy/EventController.java @@ -1,5 +1,7 @@ package com.genymobile.scrcpy; +import com.genymobile.scrcpy.wrappers.InputManager; + import android.graphics.Point; import android.os.SystemClock; import android.view.InputDevice; @@ -8,10 +10,9 @@ import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; -import com.genymobile.scrcpy.wrappers.InputManager; - import java.io.IOException; + public class EventController { private final Device device; @@ -83,6 +84,8 @@ public class EventController { case ControlEvent.TYPE_COMMAND: executeCommand(controlEvent.getAction()); break; + default: + // do nothing } } @@ -157,6 +160,8 @@ public class EventController { switch (action) { case ControlEvent.COMMAND_SCREEN_ON: return turnScreenOn(); + default: + Ln.w("Unsupported command: " + action); } return false; } diff --git a/server/src/main/java/com/genymobile/scrcpy/Ln.java b/server/src/main/java/com/genymobile/scrcpy/Ln.java index 8cf52860..38269835 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Ln.java +++ b/server/src/main/java/com/genymobile/scrcpy/Ln.java @@ -6,7 +6,7 @@ import android.util.Log; * Log both to Android logger (so that logs are visible in "adb logcat") and standard output/error (so that they are visible in the terminal * directly). */ -public class Ln { +public final class Ln { private static final String TAG = "scrcpy"; diff --git a/server/src/main/java/com/genymobile/scrcpy/Position.java b/server/src/main/java/com/genymobile/scrcpy/Position.java index 2e67b4f0..e00a6355 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Position.java +++ b/server/src/main/java/com/genymobile/scrcpy/Position.java @@ -27,11 +27,15 @@ public class Position { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } Position position = (Position) o; - return Objects.equals(point, position.point) && - Objects.equals(screenSize, position.screenSize); + return Objects.equals(point, position.point) + && Objects.equals(screenSize, position.screenSize); } @Override @@ -41,10 +45,10 @@ public class Position { @Override public String toString() { - return "Position{" + - "point=" + point + - ", screenSize=" + screenSize + - '}'; + return "Position{" + + "point=" + point + + ", screenSize=" + screenSize + + '}'; } } diff --git a/server/src/main/java/com/genymobile/scrcpy/ScrCpyServer.java b/server/src/main/java/com/genymobile/scrcpy/ScrCpyServer.java index cfe508ba..f4dd793f 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScrCpyServer.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScrCpyServer.java @@ -2,7 +2,11 @@ package com.genymobile.scrcpy; import java.io.IOException; -public class ScrCpyServer { +public final class ScrCpyServer { + + private ScrCpyServer() { + // not instantiable + } private static void scrcpy(Options options) throws IOException { final Device device = new Device(options); @@ -34,6 +38,7 @@ public class ScrCpyServer { }).start(); } + @SuppressWarnings("checkstyle:MagicNumber") private static Options createOptions(String... args) { Options options = new Options(); if (args.length < 1) { diff --git a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java index 2d056351..6aa0c276 100644 --- a/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/ScreenEncoder.java @@ -1,5 +1,7 @@ package com.genymobile.scrcpy; +import com.genymobile.scrcpy.wrappers.SurfaceControl; + import android.graphics.Rect; import android.media.MediaCodec; import android.media.MediaCodecInfo; @@ -7,8 +9,6 @@ import android.media.MediaFormat; import android.os.IBinder; import android.view.Surface; -import com.genymobile.scrcpy.wrappers.SurfaceControl; - import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; @@ -22,6 +22,8 @@ public class ScreenEncoder implements Device.RotationListener { private static final int REPEAT_FRAME_DELAY = 6; // repeat after 6 frames + private static final int MICROSECONDS_IN_ONE_SECOND = 1_000_000; + private final AtomicBoolean rotationChanged = new AtomicBoolean(); private int bitRate; @@ -81,6 +83,7 @@ public class ScreenEncoder implements Device.RotationListener { } private boolean encode(MediaCodec codec, OutputStream outputStream) throws IOException { + @SuppressWarnings("checkstyle:MagicNumber") byte[] buf = new byte[bitRate / 8]; // may contain up to 1 second of video boolean eof = false; MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); @@ -124,7 +127,7 @@ public class ScreenEncoder implements Device.RotationListener { format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval); // display the very first frame, and recover from bad quality when no new frames - format.setLong(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, 1_000_000 * REPEAT_FRAME_DELAY / frameRate); // µs + format.setLong(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, MICROSECONDS_IN_ONE_SECOND * REPEAT_FRAME_DELAY / frameRate); // µs return format; } diff --git a/server/src/main/java/com/genymobile/scrcpy/Size.java b/server/src/main/java/com/genymobile/scrcpy/Size.java index 96026d87..0d546bbd 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Size.java +++ b/server/src/main/java/com/genymobile/scrcpy/Size.java @@ -31,11 +31,15 @@ public final class Size { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } Size size = (Size) o; - return width == size.width && - height == size.height; + return width == size.width + && height == size.height; } @Override @@ -45,9 +49,9 @@ public final class Size { @Override public String toString() { - return "Size{" + - "width=" + width + - ", height=" + height + - '}'; + return "Size{" + + "width=" + width + + ", height=" + height + + '}'; } } diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java index 99dcf258..568afacd 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java @@ -1,11 +1,11 @@ package com.genymobile.scrcpy.wrappers; -import android.os.IInterface; - import com.genymobile.scrcpy.DisplayInfo; import com.genymobile.scrcpy.Size; -public class DisplayManager { +import android.os.IInterface; + +public final class DisplayManager { private final IInterface manager; public DisplayManager(IInterface manager) { diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/InputManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/InputManager.java index d34f6f59..1fc78c27 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/InputManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/InputManager.java @@ -6,7 +6,7 @@ import android.view.InputEvent; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -public class InputManager { +public final class InputManager { public static final int INJECT_INPUT_EVENT_MODE_ASYNC = 0; public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_RESULT = 1; diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/PowerManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/PowerManager.java index b53add80..a730d1b1 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/PowerManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/PowerManager.java @@ -1,19 +1,21 @@ package com.genymobile.scrcpy.wrappers; +import android.annotation.SuppressLint; import android.os.Build; import android.os.IInterface; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -public class PowerManager { +public final class PowerManager { private final IInterface manager; private final Method isScreenOnMethod; public PowerManager(IInterface manager) { this.manager = manager; try { - String methodName = Build.VERSION.SDK_INT >= 20 ? "isInteractive" : "isScreenOn"; + @SuppressLint("ObsoleteSdkInt") // we may lower minSdkVersion in the future + String methodName = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH ? "isInteractive" : "isScreenOn"; isScreenOnMethod = manager.getClass().getMethod(methodName); } catch (NoSuchMethodException e) { throw new AssertionError(e); diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/ServiceManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/ServiceManager.java index 4952893d..2d98d0a8 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/ServiceManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/ServiceManager.java @@ -1,11 +1,13 @@ package com.genymobile.scrcpy.wrappers; +import android.annotation.SuppressLint; import android.os.IBinder; import android.os.IInterface; import java.lang.reflect.Method; -public class ServiceManager { +@SuppressLint("PrivateApi") +public final class ServiceManager { private final Method getServiceMethod; private WindowManager windowManager; diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/SurfaceControl.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/SurfaceControl.java index 35efbc27..85733867 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/SurfaceControl.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/SurfaceControl.java @@ -1,16 +1,18 @@ package com.genymobile.scrcpy.wrappers; +import android.annotation.SuppressLint; import android.graphics.Rect; import android.os.IBinder; import android.view.Surface; -public class SurfaceControl { +@SuppressLint("PrivateApi") +public final class SurfaceControl { - private static final Class cls; + private static final Class CLASS; static { try { - cls = Class.forName("android.view.SurfaceControl"); + CLASS = Class.forName("android.view.SurfaceControl"); } catch (ClassNotFoundException e) { throw new AssertionError(e); } @@ -22,7 +24,7 @@ public class SurfaceControl { public static void openTransaction() { try { - cls.getMethod("openTransaction").invoke(null); + CLASS.getMethod("openTransaction").invoke(null); } catch (Exception e) { throw new AssertionError(e); } @@ -30,7 +32,7 @@ public class SurfaceControl { public static void closeTransaction() { try { - cls.getMethod("closeTransaction").invoke(null); + CLASS.getMethod("closeTransaction").invoke(null); } catch (Exception e) { throw new AssertionError(e); } @@ -38,7 +40,7 @@ public class SurfaceControl { public static void setDisplayProjection(IBinder displayToken, int orientation, Rect layerStackRect, Rect displayRect) { try { - cls.getMethod("setDisplayProjection", IBinder.class, int.class, Rect.class, Rect.class) + CLASS.getMethod("setDisplayProjection", IBinder.class, int.class, Rect.class, Rect.class) .invoke(null, displayToken, orientation, layerStackRect, displayRect); } catch (Exception e) { throw new AssertionError(e); @@ -47,7 +49,7 @@ public class SurfaceControl { public static void setDisplayLayerStack(IBinder displayToken, int layerStack) { try { - cls.getMethod("setDisplayLayerStack", IBinder.class, int.class).invoke(null, displayToken, layerStack); + CLASS.getMethod("setDisplayLayerStack", IBinder.class, int.class).invoke(null, displayToken, layerStack); } catch (Exception e) { throw new AssertionError(e); } @@ -55,7 +57,7 @@ public class SurfaceControl { public static void setDisplaySurface(IBinder displayToken, Surface surface) { try { - cls.getMethod("setDisplaySurface", IBinder.class, Surface.class).invoke(null, displayToken, surface); + CLASS.getMethod("setDisplaySurface", IBinder.class, Surface.class).invoke(null, displayToken, surface); } catch (Exception e) { throw new AssertionError(e); } @@ -63,7 +65,7 @@ public class SurfaceControl { public static IBinder createDisplay(String name, boolean secure) { try { - return (IBinder) cls.getMethod("createDisplay", String.class, boolean.class).invoke(null, name, secure); + return (IBinder) CLASS.getMethod("createDisplay", String.class, boolean.class).invoke(null, name, secure); } catch (Exception e) { throw new AssertionError(e); } @@ -71,7 +73,7 @@ public class SurfaceControl { public static void destroyDisplay(IBinder displayToken) { try { - cls.getMethod("destroyDisplay", IBinder.class).invoke(null, displayToken); + CLASS.getMethod("destroyDisplay", IBinder.class).invoke(null, displayToken); } catch (Exception e) { throw new AssertionError(e); } diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/WindowManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/WindowManager.java index 78e50c3b..56330f9d 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/WindowManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/WindowManager.java @@ -3,7 +3,7 @@ package com.genymobile.scrcpy.wrappers; import android.os.IInterface; import android.view.IRotationWatcher; -public class WindowManager { +public final class WindowManager { private final IInterface manager; public WindowManager(IInterface manager) {