Compare commits
7 Commits
tmp
...
fakecontex
Author | SHA1 | Date | |
---|---|---|---|
4fc8b150f8 | |||
f7cd88f717 | |||
b097f04459 | |||
cbabaa223e | |||
c91253105e | |||
4332b53dd5 | |||
2e9c364ef3 |
40
server/src/main/java/com/genymobile/scrcpy/FakeContext.java
Normal file
40
server/src/main/java/com/genymobile/scrcpy/FakeContext.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package com.genymobile.scrcpy;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.AttributionSource;
|
||||||
|
import android.content.ContextWrapper;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Process;
|
||||||
|
|
||||||
|
public final class FakeContext extends ContextWrapper {
|
||||||
|
|
||||||
|
public static final String PACKAGE_NAME = "com.android.shell";
|
||||||
|
|
||||||
|
private static final FakeContext INSTANCE = new FakeContext();
|
||||||
|
|
||||||
|
public static FakeContext get() {
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FakeContext() {
|
||||||
|
super(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPackageName() {
|
||||||
|
return PACKAGE_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getOpPackageName() {
|
||||||
|
return PACKAGE_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.S)
|
||||||
|
@Override
|
||||||
|
public AttributionSource getAttributionSource() {
|
||||||
|
AttributionSource.Builder builder = new AttributionSource.Builder(Process.SHELL_UID);
|
||||||
|
builder.setPackageName(PACKAGE_NAME);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
}
|
@ -67,17 +67,6 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void streamScreen(Device device, FileDescriptor fd) throws IOException {
|
public void streamScreen(Device device, FileDescriptor fd) throws IOException {
|
||||||
Workarounds.prepareMainLooper();
|
|
||||||
if (Build.BRAND.equalsIgnoreCase("meizu")) {
|
|
||||||
// <https://github.com/Genymobile/scrcpy/issues/240>
|
|
||||||
// <https://github.com/Genymobile/scrcpy/issues/2656>
|
|
||||||
Workarounds.fillAppInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
internalStreamScreen(device, fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void internalStreamScreen(Device device, FileDescriptor fd) throws IOException {
|
|
||||||
MediaCodec codec = createCodec(encoderName);
|
MediaCodec codec = createCodec(encoderName);
|
||||||
MediaFormat format = createFormat(bitRate, maxFps, codecOptions);
|
MediaFormat format = createFormat(bitRate, maxFps, codecOptions);
|
||||||
IBinder display = createDisplay();
|
IBinder display = createDisplay();
|
||||||
|
@ -66,6 +66,13 @@ public final class Server {
|
|||||||
|
|
||||||
Thread initThread = startInitThread(options);
|
Thread initThread = startInitThread(options);
|
||||||
|
|
||||||
|
Workarounds.prepareMainLooper();
|
||||||
|
if (Build.BRAND.equalsIgnoreCase("meizu")) {
|
||||||
|
// <https://github.com/Genymobile/scrcpy/issues/240>
|
||||||
|
// <https://github.com/Genymobile/scrcpy/issues/2656>
|
||||||
|
Workarounds.fillAppInfo();
|
||||||
|
}
|
||||||
|
|
||||||
int uid = options.getUid();
|
int uid = options.getUid();
|
||||||
boolean tunnelForward = options.isTunnelForward();
|
boolean tunnelForward = options.isTunnelForward();
|
||||||
boolean control = options.getControl();
|
boolean control = options.getControl();
|
||||||
|
@ -3,13 +3,11 @@ package com.genymobile.scrcpy;
|
|||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.app.Instrumentation;
|
import android.app.Instrumentation;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
|
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
|
||||||
|
|
||||||
public final class Workarounds {
|
public final class Workarounds {
|
||||||
private Workarounds() {
|
private Workarounds() {
|
||||||
@ -50,7 +48,7 @@ public final class Workarounds {
|
|||||||
Object appBindData = appBindDataConstructor.newInstance();
|
Object appBindData = appBindDataConstructor.newInstance();
|
||||||
|
|
||||||
ApplicationInfo applicationInfo = new ApplicationInfo();
|
ApplicationInfo applicationInfo = new ApplicationInfo();
|
||||||
applicationInfo.packageName = "com.genymobile.scrcpy";
|
applicationInfo.packageName = FakeContext.PACKAGE_NAME;
|
||||||
|
|
||||||
// appBindData.appInfo = applicationInfo;
|
// appBindData.appInfo = applicationInfo;
|
||||||
Field appInfoField = appBindDataClass.getDeclaredField("appInfo");
|
Field appInfoField = appBindDataClass.getDeclaredField("appInfo");
|
||||||
@ -62,11 +60,7 @@ public final class Workarounds {
|
|||||||
mBoundApplicationField.setAccessible(true);
|
mBoundApplicationField.setAccessible(true);
|
||||||
mBoundApplicationField.set(activityThread, appBindData);
|
mBoundApplicationField.set(activityThread, appBindData);
|
||||||
|
|
||||||
// Context ctx = activityThread.getSystemContext();
|
Application app = Instrumentation.newApplication(Application.class, FakeContext.get());
|
||||||
Method getSystemContextMethod = activityThreadClass.getDeclaredMethod("getSystemContext");
|
|
||||||
Context ctx = (Context) getSystemContextMethod.invoke(activityThread);
|
|
||||||
|
|
||||||
Application app = Instrumentation.newApplication(Application.class, ctx);
|
|
||||||
|
|
||||||
// activityThread.mInitialApplication = app;
|
// activityThread.mInitialApplication = app;
|
||||||
Field mInitialApplicationField = activityThreadClass.getDeclaredField("mInitialApplication");
|
Field mInitialApplicationField = activityThreadClass.getDeclaredField("mInitialApplication");
|
||||||
|
@ -5,6 +5,7 @@ import com.genymobile.scrcpy.Ln;
|
|||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.IInterface;
|
import android.os.IInterface;
|
||||||
|
import android.os.Process;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
@ -48,10 +49,10 @@ public class ActivityManager {
|
|||||||
Object[] args;
|
Object[] args;
|
||||||
if (getContentProviderExternalMethodNewVersion) {
|
if (getContentProviderExternalMethodNewVersion) {
|
||||||
// new version
|
// new version
|
||||||
args = new Object[]{name, ServiceManager.USER_ID, token, null};
|
args = new Object[]{name, Process.ROOT_UID, token, null};
|
||||||
} else {
|
} else {
|
||||||
// old version
|
// old version
|
||||||
args = new Object[]{name, ServiceManager.USER_ID, token};
|
args = new Object[]{name, Process.ROOT_UID, token};
|
||||||
}
|
}
|
||||||
// ContentProviderHolder providerHolder = getContentProviderExternal(...);
|
// ContentProviderHolder providerHolder = getContentProviderExternal(...);
|
||||||
Object providerHolder = method.invoke(manager, args);
|
Object providerHolder = method.invoke(manager, args);
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
package com.genymobile.scrcpy.wrappers;
|
package com.genymobile.scrcpy.wrappers;
|
||||||
|
|
||||||
|
import com.genymobile.scrcpy.FakeContext;
|
||||||
import com.genymobile.scrcpy.Ln;
|
import com.genymobile.scrcpy.Ln;
|
||||||
|
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
import android.content.IOnPrimaryClipChangedListener;
|
import android.content.IOnPrimaryClipChangedListener;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.IInterface;
|
import android.os.IInterface;
|
||||||
|
import android.os.Process;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
@ -58,22 +60,22 @@ public class ClipboardManager {
|
|||||||
private static ClipData getPrimaryClip(Method method, boolean alternativeMethod, IInterface manager)
|
private static ClipData getPrimaryClip(Method method, boolean alternativeMethod, IInterface manager)
|
||||||
throws InvocationTargetException, IllegalAccessException {
|
throws InvocationTargetException, IllegalAccessException {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
return (ClipData) method.invoke(manager, ServiceManager.PACKAGE_NAME);
|
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME);
|
||||||
}
|
}
|
||||||
if (alternativeMethod) {
|
if (alternativeMethod) {
|
||||||
return (ClipData) method.invoke(manager, ServiceManager.PACKAGE_NAME, null, ServiceManager.USER_ID);
|
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, Process.ROOT_UID);
|
||||||
}
|
}
|
||||||
return (ClipData) method.invoke(manager, ServiceManager.PACKAGE_NAME, ServiceManager.USER_ID);
|
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, Process.ROOT_UID);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void setPrimaryClip(Method method, boolean alternativeMethod, IInterface manager, ClipData clipData)
|
private static void setPrimaryClip(Method method, boolean alternativeMethod, IInterface manager, ClipData clipData)
|
||||||
throws InvocationTargetException, IllegalAccessException {
|
throws InvocationTargetException, IllegalAccessException {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
method.invoke(manager, clipData, ServiceManager.PACKAGE_NAME);
|
method.invoke(manager, clipData, FakeContext.PACKAGE_NAME);
|
||||||
} else if (alternativeMethod) {
|
} else if (alternativeMethod) {
|
||||||
method.invoke(manager, clipData, ServiceManager.PACKAGE_NAME, null, ServiceManager.USER_ID);
|
method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, null, Process.ROOT_UID);
|
||||||
} else {
|
} else {
|
||||||
method.invoke(manager, clipData, ServiceManager.PACKAGE_NAME, ServiceManager.USER_ID);
|
method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, Process.ROOT_UID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,11 +108,11 @@ public class ClipboardManager {
|
|||||||
private static void addPrimaryClipChangedListener(Method method, boolean alternativeMethod, IInterface manager,
|
private static void addPrimaryClipChangedListener(Method method, boolean alternativeMethod, IInterface manager,
|
||||||
IOnPrimaryClipChangedListener listener) throws InvocationTargetException, IllegalAccessException {
|
IOnPrimaryClipChangedListener listener) throws InvocationTargetException, IllegalAccessException {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
method.invoke(manager, listener, ServiceManager.PACKAGE_NAME);
|
method.invoke(manager, listener, FakeContext.PACKAGE_NAME);
|
||||||
} else if (alternativeMethod) {
|
} else if (alternativeMethod) {
|
||||||
method.invoke(manager, listener, ServiceManager.PACKAGE_NAME, null, ServiceManager.USER_ID);
|
method.invoke(manager, listener, FakeContext.PACKAGE_NAME, null, Process.ROOT_UID);
|
||||||
} else {
|
} else {
|
||||||
method.invoke(manager, listener, ServiceManager.PACKAGE_NAME, ServiceManager.USER_ID);
|
method.invoke(manager, listener, FakeContext.PACKAGE_NAME, Process.ROOT_UID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
package com.genymobile.scrcpy.wrappers;
|
package com.genymobile.scrcpy.wrappers;
|
||||||
|
|
||||||
|
import com.genymobile.scrcpy.FakeContext;
|
||||||
import com.genymobile.scrcpy.Ln;
|
import com.genymobile.scrcpy.Ln;
|
||||||
import com.genymobile.scrcpy.SettingsException;
|
import com.genymobile.scrcpy.SettingsException;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.AttributionSource;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
import android.os.Process;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
@ -51,11 +55,10 @@ public class ContentProvider implements Closeable {
|
|||||||
@SuppressLint("PrivateApi")
|
@SuppressLint("PrivateApi")
|
||||||
private Method getCallMethod() throws NoSuchMethodException {
|
private Method getCallMethod() throws NoSuchMethodException {
|
||||||
if (callMethod == null) {
|
if (callMethod == null) {
|
||||||
try {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
Class<?> attributionSourceClass = Class.forName("android.content.AttributionSource");
|
callMethod = provider.getClass().getMethod("call", AttributionSource.class, String.class, String.class, String.class, Bundle.class);
|
||||||
callMethod = provider.getClass().getMethod("call", attributionSourceClass, String.class, String.class, String.class, Bundle.class);
|
|
||||||
callMethodVersion = 0;
|
callMethodVersion = 0;
|
||||||
} catch (NoSuchMethodException | ClassNotFoundException e0) {
|
} else {
|
||||||
// old versions
|
// old versions
|
||||||
try {
|
try {
|
||||||
callMethod = provider.getClass()
|
callMethod = provider.getClass()
|
||||||
@ -75,40 +78,29 @@ public class ContentProvider implements Closeable {
|
|||||||
return callMethod;
|
return callMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("PrivateApi")
|
|
||||||
private Object getAttributionSource()
|
|
||||||
throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
|
|
||||||
if (attributionSource == null) {
|
|
||||||
Class<?> cl = Class.forName("android.content.AttributionSource$Builder");
|
|
||||||
Object builder = cl.getConstructor(int.class).newInstance(ServiceManager.USER_ID);
|
|
||||||
cl.getDeclaredMethod("setPackageName", String.class).invoke(builder, ServiceManager.PACKAGE_NAME);
|
|
||||||
attributionSource = cl.getDeclaredMethod("build").invoke(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
return attributionSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Bundle call(String callMethod, String arg, Bundle extras)
|
private Bundle call(String callMethod, String arg, Bundle extras)
|
||||||
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
|
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||||
try {
|
try {
|
||||||
Method method = getCallMethod();
|
Method method = getCallMethod();
|
||||||
Object[] args;
|
Object[] args;
|
||||||
switch (callMethodVersion) {
|
|
||||||
case 0:
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && callMethodVersion == 0) {
|
||||||
args = new Object[]{getAttributionSource(), "settings", callMethod, arg, extras};
|
args = new Object[]{FakeContext.get().getAttributionSource(), "settings", callMethod, arg, extras};
|
||||||
break;
|
} else {
|
||||||
case 1:
|
switch (callMethodVersion) {
|
||||||
args = new Object[]{ServiceManager.PACKAGE_NAME, null, "settings", callMethod, arg, extras};
|
case 1:
|
||||||
break;
|
args = new Object[]{FakeContext.PACKAGE_NAME, null, "settings", callMethod, arg, extras};
|
||||||
case 2:
|
break;
|
||||||
args = new Object[]{ServiceManager.PACKAGE_NAME, "settings", callMethod, arg, extras};
|
case 2:
|
||||||
break;
|
args = new Object[]{FakeContext.PACKAGE_NAME, "settings", callMethod, arg, extras};
|
||||||
default:
|
break;
|
||||||
args = new Object[]{ServiceManager.PACKAGE_NAME, callMethod, arg, extras};
|
default:
|
||||||
break;
|
args = new Object[]{FakeContext.PACKAGE_NAME, callMethod, arg, extras};
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return (Bundle) method.invoke(provider, args);
|
return (Bundle) method.invoke(provider, args);
|
||||||
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException | ClassNotFoundException | InstantiationException e) {
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
||||||
Ln.e("Could not invoke method", e);
|
Ln.e("Could not invoke method", e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@ -147,7 +139,7 @@ public class ContentProvider implements Closeable {
|
|||||||
public String getValue(String table, String key) throws SettingsException {
|
public String getValue(String table, String key) throws SettingsException {
|
||||||
String method = getGetMethod(table);
|
String method = getGetMethod(table);
|
||||||
Bundle arg = new Bundle();
|
Bundle arg = new Bundle();
|
||||||
arg.putInt(CALL_METHOD_USER_KEY, ServiceManager.USER_ID);
|
arg.putInt(CALL_METHOD_USER_KEY, Process.ROOT_UID);
|
||||||
try {
|
try {
|
||||||
Bundle bundle = call(method, key, arg);
|
Bundle bundle = call(method, key, arg);
|
||||||
if (bundle == null) {
|
if (bundle == null) {
|
||||||
@ -163,7 +155,7 @@ public class ContentProvider implements Closeable {
|
|||||||
public void putValue(String table, String key, String value) throws SettingsException {
|
public void putValue(String table, String key, String value) throws SettingsException {
|
||||||
String method = getPutMethod(table);
|
String method = getPutMethod(table);
|
||||||
Bundle arg = new Bundle();
|
Bundle arg = new Bundle();
|
||||||
arg.putInt(CALL_METHOD_USER_KEY, ServiceManager.USER_ID);
|
arg.putInt(CALL_METHOD_USER_KEY, Process.ROOT_UID);
|
||||||
arg.putString(NAME_VALUE_TABLE_VALUE, value);
|
arg.putString(NAME_VALUE_TABLE_VALUE, value);
|
||||||
try {
|
try {
|
||||||
call(method, key, arg);
|
call(method, key, arg);
|
||||||
|
@ -10,9 +10,6 @@ import java.lang.reflect.Method;
|
|||||||
@SuppressLint("PrivateApi,DiscouragedPrivateApi")
|
@SuppressLint("PrivateApi,DiscouragedPrivateApi")
|
||||||
public final class ServiceManager {
|
public final class ServiceManager {
|
||||||
|
|
||||||
public static final String PACKAGE_NAME = "com.android.shell";
|
|
||||||
public static final int USER_ID = 0;
|
|
||||||
|
|
||||||
private static final Method GET_SERVICE_METHOD;
|
private static final Method GET_SERVICE_METHOD;
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
|
Reference in New Issue
Block a user