From bab936194858bdf21f8505e30c38c25c26d72434 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Thu, 17 Oct 2019 21:45:52 +0200 Subject: [PATCH] Do not crash on control error Some devices do not have some methods that we invoke via reflection, or their call do not return the expected value. In that case, do not crash the whole controller. --- .../java/com/genymobile/scrcpy/Device.java | 4 ++ .../scrcpy/wrappers/ClipboardManager.java | 49 ++++++++++---- .../scrcpy/wrappers/InputManager.java | 27 ++++++-- .../scrcpy/wrappers/PowerManager.java | 31 ++++++--- .../scrcpy/wrappers/StatusBarManager.java | 40 ++++++++---- .../scrcpy/wrappers/SurfaceControl.java | 64 +++++++++++++++---- 6 files changed, 163 insertions(+), 52 deletions(-) diff --git a/server/src/main/java/com/genymobile/scrcpy/Device.java b/server/src/main/java/com/genymobile/scrcpy/Device.java index 538135d4..0246b216 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Device.java +++ b/server/src/main/java/com/genymobile/scrcpy/Device.java @@ -162,6 +162,10 @@ public final class Device { */ public void setScreenPowerMode(int mode) { IBinder d = SurfaceControl.getBuiltInDisplay(0); + if (d == null) { + Ln.e("Could not get built-in display"); + return; + } SurfaceControl.setDisplayPowerMode(d, mode); Ln.i("Device screen turned " + (mode == Device.POWER_MODE_OFF ? "off" : "on")); } diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java index a058a8bb..7dc2e75e 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java @@ -1,5 +1,7 @@ package com.genymobile.scrcpy.wrappers; +import com.genymobile.scrcpy.Ln; + import android.content.ClipData; import android.os.IInterface; @@ -8,37 +10,62 @@ import java.lang.reflect.Method; public class ClipboardManager { private final IInterface manager; - private final Method getPrimaryClipMethod; - private final Method setPrimaryClipMethod; + private Method getPrimaryClipMethod; + private Method setPrimaryClipMethod; public ClipboardManager(IInterface manager) { this.manager = manager; - try { - getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class); - setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class); - } catch (NoSuchMethodException e) { - throw new AssertionError(e); + } + + private Method getGetPrimaryClipMethod() { + if (getPrimaryClipMethod == null) { + try { + getPrimaryClipMethod = manager.getClass().getMethod("getPrimaryClip", String.class); + } catch (NoSuchMethodException e) { + Ln.e("Could not find method", e); + } } + return getPrimaryClipMethod; + } + + private Method getSetPrimaryClipMethod() { + if (setPrimaryClipMethod == null) { + try { + setPrimaryClipMethod = manager.getClass().getMethod("setPrimaryClip", ClipData.class, String.class); + } catch (NoSuchMethodException e) { + Ln.e("Could not find method", e); + } + } + return setPrimaryClipMethod; } public CharSequence getText() { + Method method = getGetPrimaryClipMethod(); + if (method == null) { + return null; + } try { - ClipData clipData = (ClipData) getPrimaryClipMethod.invoke(manager, "com.android.shell"); + ClipData clipData = (ClipData) method.invoke(manager, "com.android.shell"); if (clipData == null || clipData.getItemCount() == 0) { return null; } return clipData.getItemAt(0).getText(); } catch (InvocationTargetException | IllegalAccessException e) { - throw new AssertionError(e); + Ln.e("Could not invoke " + method.getName(), e); + return null; } } public void setText(CharSequence text) { + Method method = getSetPrimaryClipMethod(); + if (method == null) { + return; + } ClipData clipData = ClipData.newPlainText(null, text); try { - setPrimaryClipMethod.invoke(manager, clipData, "com.android.shell"); + method.invoke(manager, clipData, "com.android.shell"); } catch (InvocationTargetException | IllegalAccessException e) { - throw new AssertionError(e); + Ln.e("Could not invoke " + method.getName(), e); } } } 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 1fc78c27..788a04c7 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/InputManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/InputManager.java @@ -1,5 +1,7 @@ package com.genymobile.scrcpy.wrappers; +import com.genymobile.scrcpy.Ln; + import android.os.IInterface; import android.view.InputEvent; @@ -13,22 +15,33 @@ public final class InputManager { public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2; private final IInterface manager; - private final Method injectInputEventMethod; + private Method injectInputEventMethod; public InputManager(IInterface manager) { this.manager = manager; - try { - injectInputEventMethod = manager.getClass().getMethod("injectInputEvent", InputEvent.class, int.class); - } catch (NoSuchMethodException e) { - throw new AssertionError(e); + } + + private Method getInjectInputEventMethod() { + if (injectInputEventMethod == null) { + try { + injectInputEventMethod = manager.getClass().getMethod("injectInputEvent", InputEvent.class, int.class); + } catch (NoSuchMethodException e) { + Ln.e("Could not find method", e); + } } + return injectInputEventMethod; } public boolean injectInputEvent(InputEvent inputEvent, int mode) { + Method method = getInjectInputEventMethod(); + if (method == null) { + return false; + } try { - return (Boolean) injectInputEventMethod.invoke(manager, inputEvent, mode); + return (Boolean) method.invoke(manager, inputEvent, mode); } catch (InvocationTargetException | IllegalAccessException e) { - throw new AssertionError(e); + Ln.e("Could not invoke " + method.getName(), e); + return false; } } } 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 a730d1b1..66acdba8 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/PowerManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/PowerManager.java @@ -1,5 +1,7 @@ package com.genymobile.scrcpy.wrappers; +import com.genymobile.scrcpy.Ln; + import android.annotation.SuppressLint; import android.os.Build; import android.os.IInterface; @@ -9,24 +11,35 @@ import java.lang.reflect.Method; public final class PowerManager { private final IInterface manager; - private final Method isScreenOnMethod; + private Method isScreenOnMethod; public PowerManager(IInterface manager) { this.manager = manager; - try { - @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); + } + + private Method getIsScreenOnMethod() { + if (isScreenOnMethod == null) { + try { + @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) { + Ln.e("Could not find method", e); + } } + return isScreenOnMethod; } public boolean isScreenOn() { + Method method = getIsScreenOnMethod(); + if (method == null) { + return false; + } try { - return (Boolean) isScreenOnMethod.invoke(manager); + return (Boolean) method.invoke(manager); } catch (InvocationTargetException | IllegalAccessException e) { - throw new AssertionError(e); + Ln.e("Could not invoke " + method.getName(), e); + return false; } } } diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/StatusBarManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/StatusBarManager.java index 7cd28da6..670de952 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/StatusBarManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/StatusBarManager.java @@ -17,35 +17,49 @@ public class StatusBarManager { this.manager = manager; } - public void expandNotificationsPanel() { + private Method getExpandNotificationsPanelMethod() { if (expandNotificationsPanelMethod == null) { try { expandNotificationsPanelMethod = manager.getClass().getMethod("expandNotificationsPanel"); } catch (NoSuchMethodException e) { - Ln.e("ServiceBarManager.expandNotificationsPanel() is not available on this device"); - return; + Ln.e("Could not find method", e); } } - try { - expandNotificationsPanelMethod.invoke(manager); - } catch (InvocationTargetException | IllegalAccessException e) { - Ln.e("Could not invoke ServiceBarManager.expandNotificationsPanel()", e); - } + return expandNotificationsPanelMethod; } - public void collapsePanels() { + private Method getCollapsePanelsMethod() { if (collapsePanelsMethod == null) { try { collapsePanelsMethod = manager.getClass().getMethod("collapsePanels"); } catch (NoSuchMethodException e) { - Ln.e("ServiceBarManager.collapsePanels() is not available on this device"); - return; + Ln.e("Could not find method", e); } } + return collapsePanelsMethod; + } + + public void expandNotificationsPanel() { + Method method = getExpandNotificationsPanelMethod(); + if (method == null) { + return; + } try { - collapsePanelsMethod.invoke(manager); + method.invoke(manager); } catch (InvocationTargetException | IllegalAccessException e) { - Ln.e("Could not invoke ServiceBarManager.collapsePanels()", e); + Ln.e("Could not invoke " + method.getName(), e); + } + } + + public void collapsePanels() { + Method method = getCollapsePanelsMethod(); + if (method == null) { + return; + } + try { + method.invoke(manager); + } catch (InvocationTargetException | IllegalAccessException e) { + Ln.e("Could not invoke " + method.getName(), e); } } } 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 5b5586ff..ba37da0d 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/SurfaceControl.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/SurfaceControl.java @@ -1,11 +1,16 @@ package com.genymobile.scrcpy.wrappers; +import com.genymobile.scrcpy.Ln; + import android.annotation.SuppressLint; import android.graphics.Rect; import android.os.Build; import android.os.IBinder; import android.view.Surface; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + @SuppressLint("PrivateApi") public final class SurfaceControl { @@ -23,6 +28,9 @@ public final class SurfaceControl { } } + private static Method getBuiltInDisplayMethod; + private static Method setDisplayPowerModeMethod; + private SurfaceControl() { // only static methods } @@ -76,24 +84,56 @@ public final class SurfaceControl { } } - public static IBinder getBuiltInDisplay(int builtInDisplayId) { - try { - // the method signature has changed in Android Q - // - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { - return (IBinder) CLASS.getMethod("getBuiltInDisplay", int.class).invoke(null, builtInDisplayId); + private static Method getGetBuiltInDisplayMethod() { + if (getBuiltInDisplayMethod == null) { + try { + // the method signature has changed in Android Q + // + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + getBuiltInDisplayMethod = CLASS.getMethod("getBuiltInDisplay", int.class); + } else { + getBuiltInDisplayMethod = CLASS.getMethod("getPhysicalDisplayToken", long.class); + } + } catch (NoSuchMethodException e) { + Ln.e("Could not find method", e); } - return (IBinder) CLASS.getMethod("getPhysicalDisplayToken", long.class).invoke(null, builtInDisplayId); - } catch (Exception e) { - throw new AssertionError(e); + } + return getBuiltInDisplayMethod; + } + + public static IBinder getBuiltInDisplay(int builtInDisplayId) { + Method method = getGetBuiltInDisplayMethod(); + if (method == null) { + return null; + } + try { + return (IBinder) method.invoke(null, builtInDisplayId); + } catch (InvocationTargetException | IllegalAccessException e) { + Ln.e("Could not invoke " + method.getName(), e); + return null; } } + private static Method getSetDisplayPowerModeMethod() { + if (setDisplayPowerModeMethod == null) { + try { + setDisplayPowerModeMethod = CLASS.getMethod("setDisplayPowerMode", IBinder.class, int.class); + } catch (NoSuchMethodException e) { + Ln.e("Could not find method", e); + } + } + return setDisplayPowerModeMethod; + } + public static void setDisplayPowerMode(IBinder displayToken, int mode) { + Method method = getSetDisplayPowerModeMethod(); + if (method == null) { + return; + } try { - CLASS.getMethod("setDisplayPowerMode", IBinder.class, int.class).invoke(null, displayToken, mode); - } catch (Exception e) { - throw new AssertionError(e); + method.invoke(null, displayToken, mode); + } catch (InvocationTargetException | IllegalAccessException e) { + Ln.e("Could not invoke " + method.getName(), e); } }