diff --git a/server/build_without_gradle.sh b/server/build_without_gradle.sh index ddb98b21..e0fc3a95 100755 --- a/server/build_without_gradle.sh +++ b/server/build_without_gradle.sh @@ -50,6 +50,11 @@ cd "$SERVER_DIR/src/main/aidl" android/content/IOnPrimaryClipChangedListener.aidl "$BUILD_TOOLS_DIR/aidl" -o"$GEN_DIR" -I. android/view/IDisplayFoldListener.aidl +# Fake sources to expose hidden Android types to the project +FAKE_SRC=( \ + android/content/*java \ +) + SRC=( \ com/genymobile/scrcpy/*.java \ com/genymobile/scrcpy/audio/*.java \ @@ -72,6 +77,7 @@ javac -encoding UTF-8 -bootclasspath "$ANDROID_JAR" \ -cp "$LAMBDA_JAR:$GEN_DIR" \ -d "$CLASSES_DIR" \ -source 1.8 -target 1.8 \ + ${FAKE_SRC[@]} \ ${SRC[@]} echo "Dexing..." diff --git a/server/src/main/java/android/content/IContentProvider.java b/server/src/main/java/android/content/IContentProvider.java new file mode 100644 index 00000000..bb907dd3 --- /dev/null +++ b/server/src/main/java/android/content/IContentProvider.java @@ -0,0 +1,5 @@ +package android.content; + +public interface IContentProvider { + // android.content.IContentProvider is hidden, this is a fake one to expose the type to the project +} diff --git a/server/src/main/java/com/genymobile/scrcpy/FakeContext.java b/server/src/main/java/com/genymobile/scrcpy/FakeContext.java index 0b086cc5..2b83e397 100644 --- a/server/src/main/java/com/genymobile/scrcpy/FakeContext.java +++ b/server/src/main/java/com/genymobile/scrcpy/FakeContext.java @@ -1,9 +1,14 @@ package com.genymobile.scrcpy; +import com.genymobile.scrcpy.wrappers.ServiceManager; + import android.annotation.TargetApi; import android.content.AttributionSource; +import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; +import android.content.IContentProvider; +import android.os.Binder; import android.os.Process; public final class FakeContext extends ContextWrapper { @@ -17,6 +22,38 @@ public final class FakeContext extends ContextWrapper { return INSTANCE; } + private final ContentResolver contentResolver = new ContentResolver(this) { + @SuppressWarnings({"unused", "ProtectedMemberInFinalClass"}) + // @Override (but super-class method not visible) + protected IContentProvider acquireProvider(Context c, String name) { + return ServiceManager.getActivityManager().getContentProviderExternal(name, new Binder()); + } + + @SuppressWarnings("unused") + // @Override (but super-class method not visible) + public boolean releaseProvider(IContentProvider icp) { + return false; + } + + @SuppressWarnings({"unused", "ProtectedMemberInFinalClass"}) + // @Override (but super-class method not visible) + protected IContentProvider acquireUnstableProvider(Context c, String name) { + return null; + } + + @SuppressWarnings("unused") + // @Override (but super-class method not visible) + public boolean releaseUnstableProvider(IContentProvider icp) { + return false; + } + + @SuppressWarnings("unused") + // @Override (but super-class method not visible) + public void unstableProviderDied(IContentProvider icp) { + // ignore + } + }; + private FakeContext() { super(Workarounds.getSystemContext()); } @@ -49,4 +86,9 @@ public final class FakeContext extends ContextWrapper { public Context getApplicationContext() { return this; } + + @Override + public ContentResolver getContentResolver() { + return contentResolver; + } } diff --git a/server/src/main/java/com/genymobile/scrcpy/wrappers/ActivityManager.java b/server/src/main/java/com/genymobile/scrcpy/wrappers/ActivityManager.java index f052dee0..255483c6 100644 --- a/server/src/main/java/com/genymobile/scrcpy/wrappers/ActivityManager.java +++ b/server/src/main/java/com/genymobile/scrcpy/wrappers/ActivityManager.java @@ -6,6 +6,7 @@ import com.genymobile.scrcpy.util.Ln; import android.annotation.SuppressLint; import android.annotation.TargetApi; +import android.content.IContentProvider; import android.content.Intent; import android.os.Binder; import android.os.Bundle; @@ -64,7 +65,7 @@ public final class ActivityManager { } @TargetApi(AndroidVersions.API_29_ANDROID_10) - private ContentProvider getContentProviderExternal(String name, IBinder token) { + public IContentProvider getContentProviderExternal(String name, IBinder token) { try { Method method = getGetContentProviderExternalMethod(); Object[] args; @@ -83,11 +84,7 @@ public final class ActivityManager { // IContentProvider provider = providerHolder.provider; Field providerField = providerHolder.getClass().getDeclaredField("provider"); providerField.setAccessible(true); - Object provider = providerField.get(providerHolder); - if (provider == null) { - return null; - } - return new ContentProvider(this, provider, name, token); + return (IContentProvider) providerField.get(providerHolder); } catch (ReflectiveOperationException e) { Ln.e("Could not invoke method", e); return null; @@ -104,7 +101,12 @@ public final class ActivityManager { } public ContentProvider createSettingsProvider() { - return getContentProviderExternal("settings", new Binder()); + IBinder token = new Binder(); + IContentProvider provider = getContentProviderExternal("settings", token); + if (provider == null) { + return null; + } + return new ContentProvider(this, provider, "settings", token); } private Method getStartActivityAsUserMethod() throws NoSuchMethodException, ClassNotFoundException {