Compare commits
5 Commits
fix4392giz
...
cameraphy
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f725aebc89 | ||
|
|
4e4ddc499f | ||
|
|
8d76b3e06d | ||
|
|
85a0b935c9 | ||
|
|
8c3e2bae7b |
@@ -2,11 +2,12 @@ package com.genymobile.scrcpy;
|
|||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.content.AttributionSource;
|
import android.content.AttributionSource;
|
||||||
import android.content.MutableContextWrapper;
|
import android.content.Context;
|
||||||
|
import android.content.ContextWrapper;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
|
|
||||||
public final class FakeContext extends MutableContextWrapper {
|
public final class FakeContext extends ContextWrapper {
|
||||||
|
|
||||||
public static final String PACKAGE_NAME = "com.android.shell";
|
public static final String PACKAGE_NAME = "com.android.shell";
|
||||||
public static final int ROOT_UID = 0; // Like android.os.Process.ROOT_UID, but before API 29
|
public static final int ROOT_UID = 0; // Like android.os.Process.ROOT_UID, but before API 29
|
||||||
@@ -18,7 +19,7 @@ public final class FakeContext extends MutableContextWrapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private FakeContext() {
|
private FakeContext() {
|
||||||
super(null);
|
super(Workarounds.getSystemContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -44,4 +45,9 @@ public final class FakeContext extends MutableContextWrapper {
|
|||||||
public int getDeviceId() {
|
public int getDeviceId() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Context getApplicationContext() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,19 @@ package com.genymobile.scrcpy;
|
|||||||
import com.genymobile.scrcpy.wrappers.DisplayManager;
|
import com.genymobile.scrcpy.wrappers.DisplayManager;
|
||||||
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.hardware.camera2.CameraAccessException;
|
import android.hardware.camera2.CameraAccessException;
|
||||||
import android.hardware.camera2.CameraCharacteristics;
|
import android.hardware.camera2.CameraCharacteristics;
|
||||||
import android.hardware.camera2.CameraManager;
|
import android.hardware.camera2.CameraManager;
|
||||||
import android.hardware.camera2.params.StreamConfigurationMap;
|
import android.hardware.camera2.params.StreamConfigurationMap;
|
||||||
import android.media.MediaCodec;
|
import android.media.MediaCodec;
|
||||||
|
import android.os.Build;
|
||||||
import android.util.Range;
|
import android.util.Range;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
@@ -84,6 +88,7 @@ public final class LogUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.P)
|
||||||
public static String buildCameraListMessage(boolean includeSizes) {
|
public static String buildCameraListMessage(boolean includeSizes) {
|
||||||
StringBuilder builder = new StringBuilder("List of cameras:");
|
StringBuilder builder = new StringBuilder("List of cameras:");
|
||||||
CameraManager cameraManager = ServiceManager.getCameraManager();
|
CameraManager cameraManager = ServiceManager.getCameraManager();
|
||||||
@@ -107,6 +112,12 @@ public final class LogUtils {
|
|||||||
SortedSet<Integer> uniqueLowFps = getUniqueSet(lowFpsRanges);
|
SortedSet<Integer> uniqueLowFps = getUniqueSet(lowFpsRanges);
|
||||||
builder.append("fps=").append(uniqueLowFps).append(')');
|
builder.append("fps=").append(uniqueLowFps).append(')');
|
||||||
|
|
||||||
|
int[] capabilities = characteristics.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
|
||||||
|
if (Arrays.asList(capabilities).contains(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA)) {
|
||||||
|
Set<String> phy = characteristics.getPhysicalCameraIds();
|
||||||
|
builder.append("\n PHYSICAL: ").append(phy);
|
||||||
|
}
|
||||||
|
|
||||||
if (includeSizes) {
|
if (includeSizes) {
|
||||||
StreamConfigurationMap configs = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
|
StreamConfigurationMap configs = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
|
||||||
|
|
||||||
|
|||||||
@@ -21,18 +21,34 @@ import java.lang.reflect.Method;
|
|||||||
|
|
||||||
public final class Workarounds {
|
public final class Workarounds {
|
||||||
|
|
||||||
private static Class<?> activityThreadClass;
|
private static final Class<?> ACTIVITY_THREAD_CLASS;
|
||||||
private static Object activityThread;
|
private static final Object ACTIVITY_THREAD;
|
||||||
|
|
||||||
|
static {
|
||||||
|
prepareMainLooper();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// ActivityThread activityThread = new ActivityThread();
|
||||||
|
ACTIVITY_THREAD_CLASS = Class.forName("android.app.ActivityThread");
|
||||||
|
Constructor<?> activityThreadConstructor = ACTIVITY_THREAD_CLASS.getDeclaredConstructor();
|
||||||
|
activityThreadConstructor.setAccessible(true);
|
||||||
|
ACTIVITY_THREAD = activityThreadConstructor.newInstance();
|
||||||
|
|
||||||
|
// ActivityThread.sCurrentActivityThread = activityThread;
|
||||||
|
Field sCurrentActivityThreadField = ACTIVITY_THREAD_CLASS.getDeclaredField("sCurrentActivityThread");
|
||||||
|
sCurrentActivityThreadField.setAccessible(true);
|
||||||
|
sCurrentActivityThreadField.set(null, ACTIVITY_THREAD);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private Workarounds() {
|
private Workarounds() {
|
||||||
// not instantiable
|
// not instantiable
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void apply(boolean audio, boolean camera) {
|
public static void apply(boolean audio, boolean camera) {
|
||||||
Workarounds.prepareMainLooper();
|
|
||||||
|
|
||||||
boolean mustFillAppInfo = false;
|
boolean mustFillAppInfo = false;
|
||||||
boolean mustFillBaseContext = false;
|
|
||||||
boolean mustFillAppContext = false;
|
boolean mustFillAppContext = false;
|
||||||
|
|
||||||
if (Build.BRAND.equalsIgnoreCase("meizu")) {
|
if (Build.BRAND.equalsIgnoreCase("meizu")) {
|
||||||
@@ -53,7 +69,6 @@ public final class Workarounds {
|
|||||||
// - <https://github.com/Genymobile/scrcpy/issues/4015#issuecomment-1595382142>
|
// - <https://github.com/Genymobile/scrcpy/issues/4015#issuecomment-1595382142>
|
||||||
// - <https://github.com/Genymobile/scrcpy/issues/3805#issuecomment-1596148031>
|
// - <https://github.com/Genymobile/scrcpy/issues/3805#issuecomment-1596148031>
|
||||||
mustFillAppInfo = true;
|
mustFillAppInfo = true;
|
||||||
mustFillBaseContext = true;
|
|
||||||
mustFillAppContext = true;
|
mustFillAppContext = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,15 +81,12 @@ public final class Workarounds {
|
|||||||
|
|
||||||
if (camera) {
|
if (camera) {
|
||||||
mustFillAppInfo = true;
|
mustFillAppInfo = true;
|
||||||
mustFillBaseContext = true;
|
mustFillAppContext = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mustFillAppInfo) {
|
if (mustFillAppInfo) {
|
||||||
Workarounds.fillAppInfo();
|
Workarounds.fillAppInfo();
|
||||||
}
|
}
|
||||||
if (mustFillBaseContext) {
|
|
||||||
Workarounds.fillBaseContext();
|
|
||||||
}
|
|
||||||
if (mustFillAppContext) {
|
if (mustFillAppContext) {
|
||||||
Workarounds.fillAppContext();
|
Workarounds.fillAppContext();
|
||||||
}
|
}
|
||||||
@@ -93,27 +105,9 @@ public final class Workarounds {
|
|||||||
Looper.prepareMainLooper();
|
Looper.prepareMainLooper();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("PrivateApi,DiscouragedPrivateApi")
|
|
||||||
private static void fillActivityThread() throws Exception {
|
|
||||||
if (activityThread == null) {
|
|
||||||
// ActivityThread activityThread = new ActivityThread();
|
|
||||||
activityThreadClass = Class.forName("android.app.ActivityThread");
|
|
||||||
Constructor<?> activityThreadConstructor = activityThreadClass.getDeclaredConstructor();
|
|
||||||
activityThreadConstructor.setAccessible(true);
|
|
||||||
activityThread = activityThreadConstructor.newInstance();
|
|
||||||
|
|
||||||
// ActivityThread.sCurrentActivityThread = activityThread;
|
|
||||||
Field sCurrentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
|
|
||||||
sCurrentActivityThreadField.setAccessible(true);
|
|
||||||
sCurrentActivityThreadField.set(null, activityThread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("PrivateApi,DiscouragedPrivateApi")
|
@SuppressLint("PrivateApi,DiscouragedPrivateApi")
|
||||||
private static void fillAppInfo() {
|
private static void fillAppInfo() {
|
||||||
try {
|
try {
|
||||||
fillActivityThread();
|
|
||||||
|
|
||||||
// ActivityThread.AppBindData appBindData = new ActivityThread.AppBindData();
|
// ActivityThread.AppBindData appBindData = new ActivityThread.AppBindData();
|
||||||
Class<?> appBindDataClass = Class.forName("android.app.ActivityThread$AppBindData");
|
Class<?> appBindDataClass = Class.forName("android.app.ActivityThread$AppBindData");
|
||||||
Constructor<?> appBindDataConstructor = appBindDataClass.getDeclaredConstructor();
|
Constructor<?> appBindDataConstructor = appBindDataClass.getDeclaredConstructor();
|
||||||
@@ -129,9 +123,9 @@ public final class Workarounds {
|
|||||||
appInfoField.set(appBindData, applicationInfo);
|
appInfoField.set(appBindData, applicationInfo);
|
||||||
|
|
||||||
// activityThread.mBoundApplication = appBindData;
|
// activityThread.mBoundApplication = appBindData;
|
||||||
Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication");
|
Field mBoundApplicationField = ACTIVITY_THREAD_CLASS.getDeclaredField("mBoundApplication");
|
||||||
mBoundApplicationField.setAccessible(true);
|
mBoundApplicationField.setAccessible(true);
|
||||||
mBoundApplicationField.set(activityThread, appBindData);
|
mBoundApplicationField.set(ACTIVITY_THREAD, appBindData);
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
// this is a workaround, so failing is not an error
|
// this is a workaround, so failing is not an error
|
||||||
Ln.d("Could not fill app info: " + throwable.getMessage());
|
Ln.d("Could not fill app info: " + throwable.getMessage());
|
||||||
@@ -141,33 +135,29 @@ public final class Workarounds {
|
|||||||
@SuppressLint("PrivateApi,DiscouragedPrivateApi")
|
@SuppressLint("PrivateApi,DiscouragedPrivateApi")
|
||||||
private static void fillAppContext() {
|
private static void fillAppContext() {
|
||||||
try {
|
try {
|
||||||
fillActivityThread();
|
Application app = new Application();
|
||||||
|
|
||||||
Application app = Application.class.newInstance();
|
|
||||||
Field baseField = ContextWrapper.class.getDeclaredField("mBase");
|
Field baseField = ContextWrapper.class.getDeclaredField("mBase");
|
||||||
baseField.setAccessible(true);
|
baseField.setAccessible(true);
|
||||||
baseField.set(app, FakeContext.get());
|
baseField.set(app, FakeContext.get());
|
||||||
|
|
||||||
// activityThread.mInitialApplication = app;
|
// activityThread.mInitialApplication = app;
|
||||||
Field mInitialApplicationField = activityThreadClass.getDeclaredField("mInitialApplication");
|
Field mInitialApplicationField = ACTIVITY_THREAD_CLASS.getDeclaredField("mInitialApplication");
|
||||||
mInitialApplicationField.setAccessible(true);
|
mInitialApplicationField.setAccessible(true);
|
||||||
mInitialApplicationField.set(activityThread, app);
|
mInitialApplicationField.set(ACTIVITY_THREAD, app);
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
// this is a workaround, so failing is not an error
|
// this is a workaround, so failing is not an error
|
||||||
Ln.d("Could not fill app context: " + throwable.getMessage());
|
Ln.d("Could not fill app context: " + throwable.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void fillBaseContext() {
|
static Context getSystemContext() {
|
||||||
try {
|
try {
|
||||||
fillActivityThread();
|
Method getSystemContextMethod = ACTIVITY_THREAD_CLASS.getDeclaredMethod("getSystemContext");
|
||||||
|
return (Context) getSystemContextMethod.invoke(ACTIVITY_THREAD);
|
||||||
Method getSystemContextMethod = activityThreadClass.getDeclaredMethod("getSystemContext");
|
|
||||||
Context context = (Context) getSystemContextMethod.invoke(activityThread);
|
|
||||||
FakeContext.get().setBaseContext(context);
|
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
// this is a workaround, so failing is not an error
|
// this is a workaround, so failing is not an error
|
||||||
Ln.d("Could not fill base context: " + throwable.getMessage());
|
Ln.d("Could not get system context: " + throwable.getMessage());
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user