Compare commits
4 Commits
cameraphy
...
fix4392zxc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
338132986d | ||
|
|
3d93a099b7 | ||
|
|
29c305ca5f | ||
|
|
65d6bdb237 |
@@ -2,12 +2,11 @@ package com.genymobile.scrcpy;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.AttributionSource;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.MutableContextWrapper;
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
|
||||
public final class FakeContext extends ContextWrapper {
|
||||
public final class FakeContext extends MutableContextWrapper {
|
||||
|
||||
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
|
||||
@@ -19,7 +18,7 @@ public final class FakeContext extends ContextWrapper {
|
||||
}
|
||||
|
||||
private FakeContext() {
|
||||
super(Workarounds.getSystemContext());
|
||||
super(Workarounds.retrieveSystemContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,9 +44,4 @@ public final class FakeContext extends ContextWrapper {
|
||||
public int getDeviceId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context getApplicationContext() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,19 +3,15 @@ package com.genymobile.scrcpy;
|
||||
import com.genymobile.scrcpy.wrappers.DisplayManager;
|
||||
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.camera2.CameraAccessException;
|
||||
import android.hardware.camera2.CameraCharacteristics;
|
||||
import android.hardware.camera2.CameraManager;
|
||||
import android.hardware.camera2.params.StreamConfigurationMap;
|
||||
import android.media.MediaCodec;
|
||||
import android.os.Build;
|
||||
import android.util.Range;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
@@ -88,7 +84,6 @@ public final class LogUtils {
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.P)
|
||||
public static String buildCameraListMessage(boolean includeSizes) {
|
||||
StringBuilder builder = new StringBuilder("List of cameras:");
|
||||
CameraManager cameraManager = ServiceManager.getCameraManager();
|
||||
@@ -112,12 +107,6 @@ public final class LogUtils {
|
||||
SortedSet<Integer> uniqueLowFps = getUniqueSet(lowFpsRanges);
|
||||
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) {
|
||||
StreamConfigurationMap configs = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.content.AttributionSource;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioManager;
|
||||
import android.media.AudioRecord;
|
||||
@@ -21,34 +22,24 @@ import java.lang.reflect.Method;
|
||||
|
||||
public final class Workarounds {
|
||||
|
||||
private static final Class<?> ACTIVITY_THREAD_CLASS;
|
||||
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 static Class<?> activityThreadClass;
|
||||
private static Object activityThread;
|
||||
|
||||
private Workarounds() {
|
||||
// not instantiable
|
||||
}
|
||||
|
||||
public static void apply(boolean audio, boolean camera) {
|
||||
Workarounds.prepareMainLooper();
|
||||
try {
|
||||
fillActivityThread();
|
||||
FakeContext.get();
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
boolean mustFillAppInfo = false;
|
||||
boolean mustFillBaseContext = false;
|
||||
boolean mustFillAppContext = false;
|
||||
|
||||
if (Build.BRAND.equalsIgnoreCase("meizu")) {
|
||||
@@ -69,6 +60,7 @@ public final class Workarounds {
|
||||
// - <https://github.com/Genymobile/scrcpy/issues/4015#issuecomment-1595382142>
|
||||
// - <https://github.com/Genymobile/scrcpy/issues/3805#issuecomment-1596148031>
|
||||
mustFillAppInfo = true;
|
||||
mustFillBaseContext = true;
|
||||
mustFillAppContext = true;
|
||||
}
|
||||
|
||||
@@ -81,12 +73,15 @@ public final class Workarounds {
|
||||
|
||||
if (camera) {
|
||||
mustFillAppInfo = true;
|
||||
mustFillAppContext = true;
|
||||
mustFillBaseContext = true;
|
||||
}
|
||||
|
||||
if (mustFillAppInfo) {
|
||||
Workarounds.fillAppInfo();
|
||||
}
|
||||
if (mustFillBaseContext) {
|
||||
Workarounds.fillBaseContext();
|
||||
}
|
||||
if (mustFillAppContext) {
|
||||
Workarounds.fillAppContext();
|
||||
}
|
||||
@@ -105,9 +100,27 @@ public final class Workarounds {
|
||||
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")
|
||||
private static void fillAppInfo() {
|
||||
try {
|
||||
fillActivityThread();
|
||||
|
||||
// ActivityThread.AppBindData appBindData = new ActivityThread.AppBindData();
|
||||
Class<?> appBindDataClass = Class.forName("android.app.ActivityThread$AppBindData");
|
||||
Constructor<?> appBindDataConstructor = appBindDataClass.getDeclaredConstructor();
|
||||
@@ -117,15 +130,31 @@ public final class Workarounds {
|
||||
ApplicationInfo applicationInfo = new ApplicationInfo();
|
||||
applicationInfo.packageName = FakeContext.PACKAGE_NAME;
|
||||
|
||||
Application application = new Application() {
|
||||
@Override
|
||||
public String getOpPackageName() {
|
||||
return FakeContext.PACKAGE_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageManager getPackageManager() {
|
||||
return FakeContext.get().getPackageManager();
|
||||
}
|
||||
};
|
||||
|
||||
Field initialApplicationField = activityThreadClass.getDeclaredField("mInitialApplication");
|
||||
initialApplicationField.setAccessible(true);
|
||||
initialApplicationField.set(activityThread, application);
|
||||
|
||||
// appBindData.appInfo = applicationInfo;
|
||||
Field appInfoField = appBindDataClass.getDeclaredField("appInfo");
|
||||
appInfoField.setAccessible(true);
|
||||
appInfoField.set(appBindData, applicationInfo);
|
||||
|
||||
// activityThread.mBoundApplication = appBindData;
|
||||
Field mBoundApplicationField = ACTIVITY_THREAD_CLASS.getDeclaredField("mBoundApplication");
|
||||
Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication");
|
||||
mBoundApplicationField.setAccessible(true);
|
||||
mBoundApplicationField.set(ACTIVITY_THREAD, appBindData);
|
||||
mBoundApplicationField.set(activityThread, appBindData);
|
||||
} catch (Throwable throwable) {
|
||||
// this is a workaround, so failing is not an error
|
||||
Ln.d("Could not fill app info: " + throwable.getMessage());
|
||||
@@ -135,29 +164,33 @@ public final class Workarounds {
|
||||
@SuppressLint("PrivateApi,DiscouragedPrivateApi")
|
||||
private static void fillAppContext() {
|
||||
try {
|
||||
Application app = new Application();
|
||||
fillActivityThread();
|
||||
|
||||
Application app = Application.class.newInstance();
|
||||
Field baseField = ContextWrapper.class.getDeclaredField("mBase");
|
||||
baseField.setAccessible(true);
|
||||
baseField.set(app, FakeContext.get());
|
||||
|
||||
// activityThread.mInitialApplication = app;
|
||||
Field mInitialApplicationField = ACTIVITY_THREAD_CLASS.getDeclaredField("mInitialApplication");
|
||||
Field mInitialApplicationField = activityThreadClass.getDeclaredField("mInitialApplication");
|
||||
mInitialApplicationField.setAccessible(true);
|
||||
mInitialApplicationField.set(ACTIVITY_THREAD, app);
|
||||
mInitialApplicationField.set(activityThread, app);
|
||||
} catch (Throwable throwable) {
|
||||
// this is a workaround, so failing is not an error
|
||||
Ln.d("Could not fill app context: " + throwable.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
static Context getSystemContext() {
|
||||
private static void fillBaseContext() {
|
||||
try {
|
||||
Method getSystemContextMethod = ACTIVITY_THREAD_CLASS.getDeclaredMethod("getSystemContext");
|
||||
return (Context) getSystemContextMethod.invoke(ACTIVITY_THREAD);
|
||||
fillActivityThread();
|
||||
|
||||
Method getSystemContextMethod = activityThreadClass.getDeclaredMethod("getSystemContext");
|
||||
Context context = (Context) getSystemContextMethod.invoke(activityThread);
|
||||
FakeContext.get().setBaseContext(context);
|
||||
} catch (Throwable throwable) {
|
||||
// this is a workaround, so failing is not an error
|
||||
Ln.d("Could not get system context: " + throwable.getMessage());
|
||||
return null;
|
||||
Ln.d("Could not fill base context: " + throwable.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,4 +329,16 @@ public final class Workarounds {
|
||||
throw new RuntimeException("Cannot create AudioRecord");
|
||||
}
|
||||
}
|
||||
|
||||
static Context retrieveSystemContext() {
|
||||
try {
|
||||
Method getSystemContextMethod = activityThreadClass.getDeclaredMethod("getSystemContext");
|
||||
Context ctx = (Context) getSystemContextMethod.invoke(activityThread);
|
||||
Ln.i("===== " + ctx);
|
||||
return ctx;
|
||||
} catch (Exception e) {
|
||||
Ln.e("Cannot retrieve system context", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user