Compare commits
1 Commits
fakecontex
...
pts_client
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4fef4e0484 |
@@ -11,6 +11,8 @@
|
|||||||
/** Downcast packet_sink to recorder */
|
/** Downcast packet_sink to recorder */
|
||||||
#define DOWNCAST(SINK) container_of(SINK, struct sc_recorder, packet_sink)
|
#define DOWNCAST(SINK) container_of(SINK, struct sc_recorder, packet_sink)
|
||||||
|
|
||||||
|
#define SC_PTS_ORIGIN_NONE UINT64_C(-1)
|
||||||
|
|
||||||
static const AVRational SCRCPY_TIME_BASE = {1, 1000000}; // timestamps in us
|
static const AVRational SCRCPY_TIME_BASE = {1, 1000000}; // timestamps in us
|
||||||
|
|
||||||
static const AVOutputFormat *
|
static const AVOutputFormat *
|
||||||
@@ -128,6 +130,8 @@ sc_recorder_write(struct sc_recorder *recorder, AVPacket *packet) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOGI("==== %" PRIu64, packet->pts);
|
||||||
|
|
||||||
sc_recorder_rescale_packet(recorder, packet);
|
sc_recorder_rescale_packet(recorder, packet);
|
||||||
return av_write_frame(recorder->ctx, packet) >= 0;
|
return av_write_frame(recorder->ctx, packet) >= 0;
|
||||||
}
|
}
|
||||||
@@ -169,6 +173,18 @@ run_recorder(void *data) {
|
|||||||
|
|
||||||
sc_mutex_unlock(&recorder->mutex);
|
sc_mutex_unlock(&recorder->mutex);
|
||||||
|
|
||||||
|
if (recorder->pts_origin == SC_PTS_ORIGIN_NONE
|
||||||
|
&& rec->packet->pts != AV_NOPTS_VALUE) {
|
||||||
|
// First PTS received
|
||||||
|
recorder->pts_origin = rec->packet->pts;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rec->packet->pts != AV_NOPTS_VALUE) {
|
||||||
|
// Set PTS relatve to the origin
|
||||||
|
rec->packet->pts -= recorder->pts_origin;
|
||||||
|
rec->packet->dts = rec->packet->pts;
|
||||||
|
}
|
||||||
|
|
||||||
// recorder->previous is only written from this thread, no need to lock
|
// recorder->previous is only written from this thread, no need to lock
|
||||||
struct sc_record_packet *previous = recorder->previous;
|
struct sc_record_packet *previous = recorder->previous;
|
||||||
recorder->previous = rec;
|
recorder->previous = rec;
|
||||||
@@ -243,6 +259,7 @@ sc_recorder_open(struct sc_recorder *recorder, const AVCodec *input_codec) {
|
|||||||
recorder->failed = false;
|
recorder->failed = false;
|
||||||
recorder->header_written = false;
|
recorder->header_written = false;
|
||||||
recorder->previous = NULL;
|
recorder->previous = NULL;
|
||||||
|
recorder->pts_origin = SC_PTS_ORIGIN_NONE;
|
||||||
|
|
||||||
const char *format_name = sc_recorder_get_format_name(recorder->format);
|
const char *format_name = sc_recorder_get_format_name(recorder->format);
|
||||||
assert(format_name);
|
assert(format_name);
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ struct sc_recorder {
|
|||||||
struct sc_size declared_frame_size;
|
struct sc_size declared_frame_size;
|
||||||
bool header_written;
|
bool header_written;
|
||||||
|
|
||||||
|
uint64_t pts_origin;
|
||||||
|
|
||||||
sc_thread thread;
|
sc_thread thread;
|
||||||
sc_mutex mutex;
|
sc_mutex mutex;
|
||||||
sc_cond queue_cond;
|
sc_cond queue_cond;
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -42,7 +42,6 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||||||
private final int maxFps;
|
private final int maxFps;
|
||||||
private final boolean sendFrameMeta;
|
private final boolean sendFrameMeta;
|
||||||
private final boolean downsizeOnError;
|
private final boolean downsizeOnError;
|
||||||
private long ptsOrigin;
|
|
||||||
|
|
||||||
private boolean firstFrameSent;
|
private boolean firstFrameSent;
|
||||||
private int consecutiveErrors;
|
private int consecutiveErrors;
|
||||||
@@ -67,6 +66,17 @@ 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();
|
||||||
@@ -207,10 +217,7 @@ public class ScreenEncoder implements Device.RotationListener {
|
|||||||
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
|
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
|
||||||
pts = PACKET_FLAG_CONFIG; // non-media data packet
|
pts = PACKET_FLAG_CONFIG; // non-media data packet
|
||||||
} else {
|
} else {
|
||||||
if (ptsOrigin == 0) {
|
pts = bufferInfo.presentationTimeUs;
|
||||||
ptsOrigin = bufferInfo.presentationTimeUs;
|
|
||||||
}
|
|
||||||
pts = bufferInfo.presentationTimeUs - ptsOrigin;
|
|
||||||
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) {
|
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_KEY_FRAME) != 0) {
|
||||||
pts |= PACKET_FLAG_KEY_FRAME;
|
pts |= PACKET_FLAG_KEY_FRAME;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,13 +66,6 @@ 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,11 +3,13 @@ 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() {
|
||||||
@@ -48,7 +50,7 @@ public final class Workarounds {
|
|||||||
Object appBindData = appBindDataConstructor.newInstance();
|
Object appBindData = appBindDataConstructor.newInstance();
|
||||||
|
|
||||||
ApplicationInfo applicationInfo = new ApplicationInfo();
|
ApplicationInfo applicationInfo = new ApplicationInfo();
|
||||||
applicationInfo.packageName = FakeContext.PACKAGE_NAME;
|
applicationInfo.packageName = "com.genymobile.scrcpy";
|
||||||
|
|
||||||
// appBindData.appInfo = applicationInfo;
|
// appBindData.appInfo = applicationInfo;
|
||||||
Field appInfoField = appBindDataClass.getDeclaredField("appInfo");
|
Field appInfoField = appBindDataClass.getDeclaredField("appInfo");
|
||||||
@@ -60,7 +62,11 @@ public final class Workarounds {
|
|||||||
mBoundApplicationField.setAccessible(true);
|
mBoundApplicationField.setAccessible(true);
|
||||||
mBoundApplicationField.set(activityThread, appBindData);
|
mBoundApplicationField.set(activityThread, appBindData);
|
||||||
|
|
||||||
Application app = Instrumentation.newApplication(Application.class, FakeContext.get());
|
// Context ctx = activityThread.getSystemContext();
|
||||||
|
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,7 +5,6 @@ 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;
|
||||||
@@ -49,10 +48,10 @@ public class ActivityManager {
|
|||||||
Object[] args;
|
Object[] args;
|
||||||
if (getContentProviderExternalMethodNewVersion) {
|
if (getContentProviderExternalMethodNewVersion) {
|
||||||
// new version
|
// new version
|
||||||
args = new Object[]{name, Process.ROOT_UID, token, null};
|
args = new Object[]{name, ServiceManager.USER_ID, token, null};
|
||||||
} else {
|
} else {
|
||||||
// old version
|
// old version
|
||||||
args = new Object[]{name, Process.ROOT_UID, token};
|
args = new Object[]{name, ServiceManager.USER_ID, token};
|
||||||
}
|
}
|
||||||
// ContentProviderHolder providerHolder = getContentProviderExternal(...);
|
// ContentProviderHolder providerHolder = getContentProviderExternal(...);
|
||||||
Object providerHolder = method.invoke(manager, args);
|
Object providerHolder = method.invoke(manager, args);
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
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;
|
||||||
@@ -60,22 +58,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, FakeContext.PACKAGE_NAME);
|
return (ClipData) method.invoke(manager, ServiceManager.PACKAGE_NAME);
|
||||||
}
|
}
|
||||||
if (alternativeMethod) {
|
if (alternativeMethod) {
|
||||||
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, null, Process.ROOT_UID);
|
return (ClipData) method.invoke(manager, ServiceManager.PACKAGE_NAME, null, ServiceManager.USER_ID);
|
||||||
}
|
}
|
||||||
return (ClipData) method.invoke(manager, FakeContext.PACKAGE_NAME, Process.ROOT_UID);
|
return (ClipData) method.invoke(manager, ServiceManager.PACKAGE_NAME, ServiceManager.USER_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
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, FakeContext.PACKAGE_NAME);
|
method.invoke(manager, clipData, ServiceManager.PACKAGE_NAME);
|
||||||
} else if (alternativeMethod) {
|
} else if (alternativeMethod) {
|
||||||
method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, null, Process.ROOT_UID);
|
method.invoke(manager, clipData, ServiceManager.PACKAGE_NAME, null, ServiceManager.USER_ID);
|
||||||
} else {
|
} else {
|
||||||
method.invoke(manager, clipData, FakeContext.PACKAGE_NAME, Process.ROOT_UID);
|
method.invoke(manager, clipData, ServiceManager.PACKAGE_NAME, ServiceManager.USER_ID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,11 +106,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, FakeContext.PACKAGE_NAME);
|
method.invoke(manager, listener, ServiceManager.PACKAGE_NAME);
|
||||||
} else if (alternativeMethod) {
|
} else if (alternativeMethod) {
|
||||||
method.invoke(manager, listener, FakeContext.PACKAGE_NAME, null, Process.ROOT_UID);
|
method.invoke(manager, listener, ServiceManager.PACKAGE_NAME, null, ServiceManager.USER_ID);
|
||||||
} else {
|
} else {
|
||||||
method.invoke(manager, listener, FakeContext.PACKAGE_NAME, Process.ROOT_UID);
|
method.invoke(manager, listener, ServiceManager.PACKAGE_NAME, ServiceManager.USER_ID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
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;
|
||||||
@@ -55,10 +51,11 @@ 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) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
try {
|
||||||
callMethod = provider.getClass().getMethod("call", AttributionSource.class, String.class, String.class, String.class, Bundle.class);
|
Class<?> attributionSourceClass = Class.forName("android.content.AttributionSource");
|
||||||
|
callMethod = provider.getClass().getMethod("call", attributionSourceClass, String.class, String.class, String.class, Bundle.class);
|
||||||
callMethodVersion = 0;
|
callMethodVersion = 0;
|
||||||
} else {
|
} catch (NoSuchMethodException | ClassNotFoundException e0) {
|
||||||
// old versions
|
// old versions
|
||||||
try {
|
try {
|
||||||
callMethod = provider.getClass()
|
callMethod = provider.getClass()
|
||||||
@@ -78,29 +75,40 @@ 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 NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
|
||||||
try {
|
try {
|
||||||
Method method = getCallMethod();
|
Method method = getCallMethod();
|
||||||
Object[] args;
|
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) {
|
switch (callMethodVersion) {
|
||||||
|
case 0:
|
||||||
|
args = new Object[]{getAttributionSource(), "settings", callMethod, arg, extras};
|
||||||
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
args = new Object[]{FakeContext.PACKAGE_NAME, null, "settings", callMethod, arg, extras};
|
args = new Object[]{ServiceManager.PACKAGE_NAME, null, "settings", callMethod, arg, extras};
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
args = new Object[]{FakeContext.PACKAGE_NAME, "settings", callMethod, arg, extras};
|
args = new Object[]{ServiceManager.PACKAGE_NAME, "settings", callMethod, arg, extras};
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
args = new Object[]{FakeContext.PACKAGE_NAME, callMethod, arg, extras};
|
args = new Object[]{ServiceManager.PACKAGE_NAME, callMethod, arg, extras};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return (Bundle) method.invoke(provider, args);
|
return (Bundle) method.invoke(provider, args);
|
||||||
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
|
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException | ClassNotFoundException | InstantiationException e) {
|
||||||
Ln.e("Could not invoke method", e);
|
Ln.e("Could not invoke method", e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@@ -139,7 +147,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, Process.ROOT_UID);
|
arg.putInt(CALL_METHOD_USER_KEY, ServiceManager.USER_ID);
|
||||||
try {
|
try {
|
||||||
Bundle bundle = call(method, key, arg);
|
Bundle bundle = call(method, key, arg);
|
||||||
if (bundle == null) {
|
if (bundle == null) {
|
||||||
@@ -155,7 +163,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, Process.ROOT_UID);
|
arg.putInt(CALL_METHOD_USER_KEY, ServiceManager.USER_ID);
|
||||||
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,6 +10,9 @@ 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