Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
d61c2ab3f9 | ||
|
e6cca8db5c |
@ -7,7 +7,6 @@ import android.media.MediaCodec;
|
|||||||
import android.media.MediaCodecInfo;
|
import android.media.MediaCodecInfo;
|
||||||
import android.media.MediaFormat;
|
import android.media.MediaFormat;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
import java.io.FileDescriptor;
|
||||||
@ -55,15 +54,8 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void streamScreen(Device device, FileDescriptor fd) throws IOException {
|
public void streamScreen(Device device, FileDescriptor fd) throws IOException {
|
||||||
// Some devices internally create a Handler when creating an input Surface, causing an exception:
|
Workarounds.prepareMainLooper();
|
||||||
// "Can't create handler inside thread that has not called Looper.prepare()"
|
Workarounds.fillAppInfo();
|
||||||
// <https://github.com/Genymobile/scrcpy/issues/240>
|
|
||||||
//
|
|
||||||
// Use Looper.prepareMainLooper() instead of Looper.prepare() to avoid a NullPointerException:
|
|
||||||
// "Attempt to read from field 'android.os.MessageQueue android.os.Looper.mQueue'
|
|
||||||
// on a null object reference"
|
|
||||||
// <https://github.com/Genymobile/scrcpy/issues/921>
|
|
||||||
Looper.prepareMainLooper();
|
|
||||||
|
|
||||||
MediaFormat format = createFormat(bitRate, frameRate, iFrameInterval);
|
MediaFormat format = createFormat(bitRate, frameRate, iFrameInterval);
|
||||||
device.setRotationListener(this);
|
device.setRotationListener(this);
|
||||||
|
64
server/src/main/java/com/genymobile/scrcpy/Workarounds.java
Normal file
64
server/src/main/java/com/genymobile/scrcpy/Workarounds.java
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package com.genymobile.scrcpy;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.os.Looper;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
public final class Workarounds {
|
||||||
|
private Workarounds() {
|
||||||
|
// not instantiable
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void prepareMainLooper() {
|
||||||
|
// Some devices internally create a Handler when creating an input Surface, causing an exception:
|
||||||
|
// "Can't create handler inside thread that has not called Looper.prepare()"
|
||||||
|
// <https://github.com/Genymobile/scrcpy/issues/240>
|
||||||
|
//
|
||||||
|
// Use Looper.prepareMainLooper() instead of Looper.prepare() to avoid a NullPointerException:
|
||||||
|
// "Attempt to read from field 'android.os.MessageQueue android.os.Looper.mQueue'
|
||||||
|
// on a null object reference"
|
||||||
|
// <https://github.com/Genymobile/scrcpy/issues/921>
|
||||||
|
Looper.prepareMainLooper();
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("PrivateApi")
|
||||||
|
public static void fillAppInfo() {
|
||||||
|
try {
|
||||||
|
// ActivityThread activityThread = new ActivityThread();
|
||||||
|
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
|
||||||
|
Constructor<?> activityThreadConstructor = activityThreadClass.getDeclaredConstructor();
|
||||||
|
activityThreadConstructor.setAccessible(true);
|
||||||
|
Object activityThread = activityThreadConstructor.newInstance();
|
||||||
|
|
||||||
|
// ActivityThread.sCurrentActivityThread = activityThread;
|
||||||
|
Field sCurrentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
|
||||||
|
sCurrentActivityThreadField.setAccessible(true);
|
||||||
|
sCurrentActivityThreadField.set(null, activityThread);
|
||||||
|
|
||||||
|
// ActivityThread.AppBindData appBindData = new ActivityThread.AppBindData();
|
||||||
|
Class<?> appBindDataClass = Class.forName("android.app.ActivityThread$AppBindData");
|
||||||
|
Constructor<?> appBindDataConstructor = appBindDataClass.getDeclaredConstructor();
|
||||||
|
appBindDataConstructor.setAccessible(true);
|
||||||
|
Object appBindData = appBindDataConstructor.newInstance();
|
||||||
|
|
||||||
|
ApplicationInfo applicationInfo = new ApplicationInfo();
|
||||||
|
applicationInfo.packageName = "com.genymobile.scrcpy";
|
||||||
|
|
||||||
|
// appBindData.appInfo = applicationInfo;
|
||||||
|
Field appInfo = appBindDataClass.getDeclaredField("appInfo");
|
||||||
|
appInfo.setAccessible(true);
|
||||||
|
appInfo.set(appBindData, applicationInfo);
|
||||||
|
|
||||||
|
// activityThread.mBoundApplication = appBindData;
|
||||||
|
Field mBoundApplicationField = activityThreadClass.getDeclaredField("mBoundApplication");
|
||||||
|
mBoundApplicationField.setAccessible(true);
|
||||||
|
mBoundApplicationField.set(activityThread, appBindData);
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
// this is a workaround, so failing is not an error
|
||||||
|
Ln.w("Could not fill app info: " + throwable.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user