Add FakeContext.getContentResolver()

This avoids the following error on some devices:

    Given calling package android does not match caller's uid 2000

Refs #4639 comment <https://github.com/Genymobile/scrcpy/issues/4639#issuecomment-2466081589>
Fixes #4639 <https://github.com/Genymobile/scrcpy/issues/4639>
PR #5476 <https://github.com/Genymobile/scrcpy/pull/5476>

Signed-off-by: Romain Vimont <rom@rom1v.com>
This commit is contained in:
Simon Chan 2024-11-09 14:34:55 +08:00 committed by Romain Vimont
parent c0e2e27cf9
commit 91373d906b
4 changed files with 62 additions and 7 deletions

View File

@ -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..."

View File

@ -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
}

View File

@ -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;
}
}

View File

@ -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 {