From 83d6db428f596fced901f4afdac53d622209998e Mon Sep 17 00:00:00 2001 From: Roberto Viola Date: Fri, 21 Jun 2024 15:59:37 +0200 Subject: [PATCH] add deps --- .../org/cagnulein/android_remote/Device.java | 5 +- .../wrappers/ContentProvider.java | 161 ++++++++++++++++++ .../wrappers/DisplayManager.java | 14 +- .../wrappers/ServiceManager.java | 5 +- 4 files changed, 178 insertions(+), 7 deletions(-) create mode 100644 server/src/main/java/org/cagnulein/android_remote/wrappers/ContentProvider.java diff --git a/server/src/main/java/org/cagnulein/android_remote/Device.java b/server/src/main/java/org/cagnulein/android_remote/Device.java index a9f5ba9..94237fa 100644 --- a/server/src/main/java/org/cagnulein/android_remote/Device.java +++ b/server/src/main/java/org/cagnulein/android_remote/Device.java @@ -18,6 +18,8 @@ public final class Device { private ScreenInfo screenInfo; private RotationListener rotationListener; + private final int displayId; + public Device(Options options) { screenInfo = computeScreenInfo(options.getMaxSize()); setScreenPowerMode(POWER_MODE_OFF); @@ -51,7 +53,8 @@ private ScreenInfo computeScreenInfo(int maxSize) { // - scale down the great side of the screen to maxSize (if necessary); // - scale down the other side so that the aspect ratio is preserved; // - round this value to the nearest multiple of 8 (H.264 only accepts multiples of 8) - DisplayInfo displayInfo = serviceManager.getDisplayManager().getDisplayInfo(); + displayId = options.getDisplayId(); + DisplayInfo displayInfo = ServiceManager.getDisplayManager().getDisplayInfo(displayId); boolean rotated = (displayInfo.getRotation() & 1) != 0; Size deviceSize = displayInfo.getSize(); int w = deviceSize.getWidth() & ~7; // in case it's not a multiple of 8 diff --git a/server/src/main/java/org/cagnulein/android_remote/wrappers/ContentProvider.java b/server/src/main/java/org/cagnulein/android_remote/wrappers/ContentProvider.java new file mode 100644 index 0000000..ca870a3 --- /dev/null +++ b/server/src/main/java/org/cagnulein/android_remote/wrappers/ContentProvider.java @@ -0,0 +1,161 @@ +package org.cagnulein.android_remote.wrappers; + +import org.cagnulein.android_remote.FakeContext; +import org.cagnulein.android_remote.Ln; +import org.cagnulein.android_remote.SettingsException; + +import android.annotation.SuppressLint; +import android.content.AttributionSource; +import android.os.Build; +import android.os.Bundle; +import android.os.IBinder; + +import java.io.Closeable; +import java.lang.reflect.Method; + +public final class ContentProvider implements Closeable { + + public static final String TABLE_SYSTEM = "system"; + public static final String TABLE_SECURE = "secure"; + public static final String TABLE_GLOBAL = "global"; + + // See android/providerHolder/Settings.java + private static final String CALL_METHOD_GET_SYSTEM = "GET_system"; + private static final String CALL_METHOD_GET_SECURE = "GET_secure"; + private static final String CALL_METHOD_GET_GLOBAL = "GET_global"; + + private static final String CALL_METHOD_PUT_SYSTEM = "PUT_system"; + private static final String CALL_METHOD_PUT_SECURE = "PUT_secure"; + private static final String CALL_METHOD_PUT_GLOBAL = "PUT_global"; + + private static final String CALL_METHOD_USER_KEY = "_user"; + + private static final String NAME_VALUE_TABLE_VALUE = "value"; + + private final ActivityManager manager; + // android.content.IContentProvider + private final Object provider; + private final String name; + private final IBinder token; + + private Method callMethod; + private int callMethodVersion; + + ContentProvider(ActivityManager manager, Object provider, String name, IBinder token) { + this.manager = manager; + this.provider = provider; + this.name = name; + this.token = token; + } + + @SuppressLint("PrivateApi") + private Method getCallMethod() throws NoSuchMethodException { + if (callMethod == null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + callMethod = provider.getClass().getMethod("call", AttributionSource.class, String.class, String.class, String.class, Bundle.class); + callMethodVersion = 0; + } else { + // old versions + try { + callMethod = provider.getClass() + .getMethod("call", String.class, String.class, String.class, String.class, String.class, Bundle.class); + callMethodVersion = 1; + } catch (NoSuchMethodException e1) { + try { + callMethod = provider.getClass().getMethod("call", String.class, String.class, String.class, String.class, Bundle.class); + callMethodVersion = 2; + } catch (NoSuchMethodException e2) { + callMethod = provider.getClass().getMethod("call", String.class, String.class, String.class, Bundle.class); + callMethodVersion = 3; + } + } + } + } + return callMethod; + } + + private Bundle call(String callMethod, String arg, Bundle extras) throws ReflectiveOperationException { + try { + Method method = getCallMethod(); + Object[] args; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && callMethodVersion == 0) { + args = new Object[]{FakeContext.get().getAttributionSource(), "settings", callMethod, arg, extras}; + } else { + switch (callMethodVersion) { + case 1: + args = new Object[]{FakeContext.PACKAGE_NAME, null, "settings", callMethod, arg, extras}; + break; + case 2: + args = new Object[]{FakeContext.PACKAGE_NAME, "settings", callMethod, arg, extras}; + break; + default: + args = new Object[]{FakeContext.PACKAGE_NAME, callMethod, arg, extras}; + break; + } + } + return (Bundle) method.invoke(provider, args); + } catch (ReflectiveOperationException e) { + Ln.e("Could not invoke method", e); + throw e; + } + } + + public void close() { + manager.removeContentProviderExternal(name, token); + } + + private static String getGetMethod(String table) { + switch (table) { + case TABLE_SECURE: + return CALL_METHOD_GET_SECURE; + case TABLE_SYSTEM: + return CALL_METHOD_GET_SYSTEM; + case TABLE_GLOBAL: + return CALL_METHOD_GET_GLOBAL; + default: + throw new IllegalArgumentException("Invalid table: " + table); + } + } + + private static String getPutMethod(String table) { + switch (table) { + case TABLE_SECURE: + return CALL_METHOD_PUT_SECURE; + case TABLE_SYSTEM: + return CALL_METHOD_PUT_SYSTEM; + case TABLE_GLOBAL: + return CALL_METHOD_PUT_GLOBAL; + default: + throw new IllegalArgumentException("Invalid table: " + table); + } + } + + public String getValue(String table, String key) throws SettingsException { + String method = getGetMethod(table); + Bundle arg = new Bundle(); + arg.putInt(CALL_METHOD_USER_KEY, FakeContext.ROOT_UID); + try { + Bundle bundle = call(method, key, arg); + if (bundle == null) { + return null; + } + return bundle.getString("value"); + } catch (Exception e) { + throw new SettingsException(table, "get", key, null, e); + } + + } + + public void putValue(String table, String key, String value) throws SettingsException { + String method = getPutMethod(table); + Bundle arg = new Bundle(); + arg.putInt(CALL_METHOD_USER_KEY, FakeContext.ROOT_UID); + arg.putString(NAME_VALUE_TABLE_VALUE, value); + try { + call(method, key, arg); + } catch (Exception e) { + throw new SettingsException(table, "put", key, value, e); + } + } +} \ No newline at end of file diff --git a/server/src/main/java/org/cagnulein/android_remote/wrappers/DisplayManager.java b/server/src/main/java/org/cagnulein/android_remote/wrappers/DisplayManager.java index 5ab48cf..039e20e 100644 --- a/server/src/main/java/org/cagnulein/android_remote/wrappers/DisplayManager.java +++ b/server/src/main/java/org/cagnulein/android_remote/wrappers/DisplayManager.java @@ -26,16 +26,22 @@ public static VirtualDisplay createVirtualDisplay(String name, int width, int he .invoke(null, name, width, height, displayIdToMirror, surface); } - public DisplayInfo getDisplayInfo() { + public DisplayInfo getDisplayInfo(int displayId) { try { - Object displayInfo = manager.getClass().getMethod("getDisplayInfo", int.class).invoke(manager, 0); + Object displayInfo = manager.getClass().getMethod("getDisplayInfo", int.class).invoke(manager, displayId); + if (displayInfo == null) { + // fallback when displayInfo is null + return getDisplayInfoFromDumpsysDisplay(displayId); + } Class cls = displayInfo.getClass(); // width and height already take the rotation into account int width = cls.getDeclaredField("logicalWidth").getInt(displayInfo); int height = cls.getDeclaredField("logicalHeight").getInt(displayInfo); int rotation = cls.getDeclaredField("rotation").getInt(displayInfo); - return new DisplayInfo(new Size(width, height), rotation); - } catch (Exception e) { + int layerStack = cls.getDeclaredField("layerStack").getInt(displayInfo); + int flags = cls.getDeclaredField("flags").getInt(displayInfo); + return new DisplayInfo(displayId, new Size(width, height), rotation, layerStack, flags); + } catch (ReflectiveOperationException e) { throw new AssertionError(e); } } diff --git a/server/src/main/java/org/cagnulein/android_remote/wrappers/ServiceManager.java b/server/src/main/java/org/cagnulein/android_remote/wrappers/ServiceManager.java index 9658f63..9b43973 100644 --- a/server/src/main/java/org/cagnulein/android_remote/wrappers/ServiceManager.java +++ b/server/src/main/java/org/cagnulein/android_remote/wrappers/ServiceManager.java @@ -5,6 +5,7 @@ import android.os.IInterface; import java.lang.reflect.Method; +import android.hardware.camera2.CameraManager; @SuppressLint("PrivateApi") public final class ServiceManager { @@ -25,7 +26,7 @@ public ServiceManager() { } } - private static IInterface getService(String service, String type) { + private IInterface getService(String service, String type) { try { IBinder binder = (IBinder) getServiceMethod.invoke(null, service); Method asInterfaceMethod = Class.forName(type + "$Stub").getMethod("asInterface", IBinder.class); @@ -68,7 +69,7 @@ public static CameraManager getCameraManager() { return cameraManager; } - public static owerManager getPowerManager() { + public static PowerManager getPowerManager() { if (powerManager == null) { powerManager = new PowerManager(getService("power", "android.os.IPowerManager")); }