Adapt call() on ContentProvider for Android 12

Android 12 changed one of the call() overloads with a new parameter
AttributionSource. Adapt the wrapper.

Fixes #2402 <https://github.com/Genymobile/scrcpy/issues/2402>
This commit is contained in:
Romain Vimont 2021-06-19 08:43:49 +02:00
parent a1f2094787
commit b846d3a085

View file

@ -2,6 +2,7 @@ package com.genymobile.scrcpy.wrappers;
import com.genymobile.scrcpy.Ln; import com.genymobile.scrcpy.Ln;
import android.annotation.SuppressLint;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
@ -37,6 +38,8 @@ public class ContentProvider implements Closeable {
private Method callMethod; private Method callMethod;
private int callMethodVersion; private int callMethodVersion;
private Object attributionSource;
ContentProvider(ActivityManager manager, Object provider, String name, IBinder token) { ContentProvider(ActivityManager manager, Object provider, String name, IBinder token) {
this.manager = manager; this.manager = manager;
this.provider = provider; this.provider = provider;
@ -44,36 +47,58 @@ public class ContentProvider implements Closeable {
this.token = token; this.token = token;
} }
@SuppressLint("PrivateApi")
private Method getCallMethod() throws NoSuchMethodException { private Method getCallMethod() throws NoSuchMethodException {
if (callMethod == null) { if (callMethod == null) {
try { try {
callMethod = provider.getClass() Class<?> attributionSourceClass = Class.forName("android.content.AttributionSource");
.getMethod("call", String.class, String.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 e) { } catch (NoSuchMethodException | ClassNotFoundException e0) {
// old versions // old versions
try { try {
callMethod = provider.getClass().getMethod("call", String.class, String.class, String.class, String.class, Bundle.class); callMethod = provider.getClass()
.getMethod("call", String.class, String.class, String.class, String.class, String.class, Bundle.class);
callMethodVersion = 1; callMethodVersion = 1;
} catch (NoSuchMethodException e2) { } catch (NoSuchMethodException e1) {
callMethod = provider.getClass().getMethod("call", String.class, String.class, String.class, Bundle.class); try {
callMethodVersion = 2; callMethod = provider.getClass().getMethod("call", String.class, String.class, String.class, String.class, Bundle.class);
callMethodVersion = 2;
} catch (NoSuchMethodException e2) {
callMethod = provider.getClass().getMethod("call", String.class, String.class, String.class, Bundle.class);
callMethodVersion = 3;
}
} }
} }
} }
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) {
try { try {
Method method = getCallMethod(); Method method = getCallMethod();
Object[] args; Object[] args;
switch (callMethodVersion) { switch (callMethodVersion) {
case 0: case 0:
args = new Object[]{ServiceManager.PACKAGE_NAME, null, "settings", callMethod, arg, extras}; args = new Object[]{getAttributionSource(), "settings", callMethod, arg, extras};
break; break;
case 1: case 1:
args = new Object[]{ServiceManager.PACKAGE_NAME, null, "settings", callMethod, arg, extras};
break;
case 2:
args = new Object[]{ServiceManager.PACKAGE_NAME, "settings", callMethod, arg, extras}; args = new Object[]{ServiceManager.PACKAGE_NAME, "settings", callMethod, arg, extras};
break; break;
default: default:
@ -81,7 +106,7 @@ public class ContentProvider implements Closeable {
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);
return null; return null;
} }