diff --git a/annotations/src/main/java/org/robolectric/annotation/ClassName.java b/annotations/src/main/java/org/robolectric/annotation/ClassName.java new file mode 100644 index 00000000000..7f6b2c5611f --- /dev/null +++ b/annotations/src/main/java/org/robolectric/annotation/ClassName.java @@ -0,0 +1,28 @@ +package org.robolectric.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicate a real class name of method's input parameter. + * + *
For some important Android framework's shadow class, we might bring new APIs to current shadow + * class, but these APIs might be added from newer SDK version, and it will cause compiling error + * when using these shadow classes with lower compileSdk. We can use this annotation and Object type + * to avoid compiling error but with implicit type checking. + */ +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.PARAMETER}) +public @interface ClassName { + /** + * The class name intended for this parameter. + * + *
Use the value as returned from {@link Class#getName()}, not {@link + * Class#getCanonicalName()}; e.g. {@code Foo$Bar} instead of {@code Foo.Bar}. + */ + String value() default ""; +} diff --git a/sandbox/src/main/java/org/robolectric/internal/bytecode/ShadowWrangler.java b/sandbox/src/main/java/org/robolectric/internal/bytecode/ShadowWrangler.java index 5e03aa945d7..7a41d47066e 100644 --- a/sandbox/src/main/java/org/robolectric/internal/bytecode/ShadowWrangler.java +++ b/sandbox/src/main/java/org/robolectric/internal/bytecode/ShadowWrangler.java @@ -8,6 +8,7 @@ import static org.robolectric.util.reflector.Reflector.reflector; import com.google.auto.service.AutoService; +import java.lang.annotation.Annotation; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -21,6 +22,7 @@ import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Priority; +import org.robolectric.annotation.ClassName; import org.robolectric.annotation.RealObject; import org.robolectric.annotation.ReflectorObject; import org.robolectric.sandbox.NativeMethodNotFoundException; @@ -322,9 +324,16 @@ private Method findShadowMethod( Class> shadowClass) { Method method = findShadowMethodDeclaredOnClass(shadowClass, name, types); - if (method == null && shadowInfo.looseSignatures) { - Class>[] genericTypes = MethodType.genericMethodType(types.length).parameterArray(); - method = findShadowMethodDeclaredOnClass(shadowClass, name, genericTypes); + if (method == null) { + // If user sets looseSignatures for shadow class, we will try to find method with generic + // types again. + if (shadowInfo.looseSignatures) { + Class>[] genericTypes = MethodType.genericMethodType(types.length).parameterArray(); + method = findShadowMethodDeclaredOnClass(shadowClass, name, genericTypes); + } else { + // Otherwise, we will try to find method with ClassName annotation that can match signature. + method = findShadowMethodWithClassNameDeclaredOnClass(shadowClass, name, types); + } } if (method != null) { @@ -348,6 +357,76 @@ private Method findShadowMethod( return method; } + private ClassName finClassNameAnnotation(Annotation[] annotations) { + for (Annotation annotation : annotations) { + if (ClassName.class.isAssignableFrom(annotation.annotationType())) { + return (ClassName) annotation; + } + } + return null; + } + + private boolean hasClassNameAnnotation(Annotation[][] annotations) { + for (Annotation[] parameterAnnotations : annotations) { + for (Annotation annotation : parameterAnnotations) { + if (ClassName.class.isAssignableFrom(annotation.annotationType())) { + return true; + } + } + } + return false; + } + + private Method findShadowMethodWithClassNameDeclaredOnClass( + Class> shadowClass, String methodName, Class>[] paramClasses) { + // We don't process the method without input parameters now. + if (paramClasses == null || paramClasses.length == 0) { + return null; + } + Method[] methods = shadowClass.getDeclaredMethods(); + for (Method method : methods) { + if (method == null + || !method.getName().equals(methodName) + || method.getParameterCount() != paramClasses.length + || !isValidShadowMethod(method)) { + continue; + } + Class>[] parameterTypes = method.getParameterTypes(); + Annotation[][] allAnnotations = method.getParameterAnnotations(); + if (!hasClassNameAnnotation(allAnnotations)) { + continue; + } + boolean matched = true; + for (int i = 0; i < parameterTypes.length; i++) { + // If method's parameter type is superclass of input parameter, we can pass checking for + // this parameter. + if (parameterTypes[i].isAssignableFrom(paramClasses[i])) { + continue; + } + if (allAnnotations.length <= i) { + matched = false; + break; + } + ClassName className = finClassNameAnnotation(allAnnotations[i]); + try { + if (className != null + && Class.forName(className.value()).isAssignableFrom(paramClasses[i])) { + continue; + } + } catch (ClassNotFoundException ignored) { + // Do nothing + } + matched = false; + break; + } + // TODO identify why above logic will affect __constructor__ without ClassName + if (matched) { + return method; + } + } + return null; + } + private Method findShadowMethodDeclaredOnClass( Class> shadowClass, String methodName, Class>[] paramClasses) { try { diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivity.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivity.java index 0e0d4e605f9..6b381d14078 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivity.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivity.java @@ -81,7 +81,7 @@ import org.robolectric.util.reflector.WithType; @SuppressWarnings("NewApi") -@Implements(value = Activity.class, looseSignatures = true) +@Implements(value = Activity.class) public class ShadowActivity extends ShadowContextThemeWrapper { @RealObject protected Activity realActivity; diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivityThread.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivityThread.java index 8b55be12417..d8af12ccc18 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivityThread.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowActivityThread.java @@ -42,7 +42,7 @@ import org.robolectric.util.reflector.ForType; import org.robolectric.util.reflector.Reflector; -@Implements(value = ActivityThread.class, isInAndroidSdk = false, looseSignatures = true) +@Implements(value = ActivityThread.class, isInAndroidSdk = false) public class ShadowActivityThread { private static ApplicationInfo applicationInfo; @RealObject protected ActivityThread realActivityThread; diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAppIntegrityManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAppIntegrityManager.java index 1065554d327..4d7eaa507a2 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAppIntegrityManager.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAppIntegrityManager.java @@ -13,7 +13,6 @@ @Implements( value = AppIntegrityManager.class, minSdk = R, - looseSignatures = true, isInAndroidSdk = false) public class ShadowAppIntegrityManager { diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAppOpsManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAppOpsManager.java index 0d36bfc0604..52a250aed85 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAppOpsManager.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAppOpsManager.java @@ -53,6 +53,7 @@ import java.util.Set; import java.util.stream.IntStream; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.ClassName; import org.robolectric.annotation.HiddenApi; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; @@ -65,7 +66,7 @@ import org.robolectric.util.reflector.ForType; /** Shadow for {@link AppOpsManager}. */ -@Implements(value = AppOpsManager.class, minSdk = KITKAT, looseSignatures = true) +@Implements(value = AppOpsManager.class, minSdk = KITKAT) public class ShadowAppOpsManager { // OpEntry fields that the shadow doesn't currently allow the test to configure. @@ -416,18 +417,18 @@ protected int noteProxyOpNoThrow( @RequiresApi(api = S) @Implementation(minSdk = S) protected int noteProxyOpNoThrow( - Object op, Object attributionSource, Object message, Object ignoredSkipProxyOperation) { - Preconditions.checkArgument(op instanceof Integer); + int op, + @ClassName(value = "android.content.AttributionSource ") Object attributionSource, + String message, + boolean ignoredSkipProxyOperation) { Preconditions.checkArgument(attributionSource instanceof AttributionSource); - Preconditions.checkArgument(message == null || message instanceof String); - Preconditions.checkArgument(ignoredSkipProxyOperation instanceof Boolean); AttributionSource castedAttributionSource = (AttributionSource) attributionSource; return noteProxyOpNoThrow( - (int) op, + op, castedAttributionSource.getNextPackageName(), castedAttributionSource.getNextUid(), castedAttributionSource.getNextAttributionTag(), - (String) message); + message); } @Implementation diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowArscApkAssets9.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowArscApkAssets9.java index 9ab0887c72a..9ff99c33227 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowArscApkAssets9.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowArscApkAssets9.java @@ -14,6 +14,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.ClassName; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.annotation.RealObject; @@ -37,8 +38,7 @@ value = ApkAssets.class, minSdk = P, shadowPicker = Picker.class, - isInAndroidSdk = false, - looseSignatures = true) + isInAndroidSdk = false) public class ShadowArscApkAssets9 extends ShadowApkAssets { // #define ATRACE_TAG ATRACE_TAG_RESOURCES // @@ -150,11 +150,15 @@ protected static long nativeLoad( @Implementation(minSdk = R) protected static Object nativeLoad( - Object format, Object javaPath, Object flags, Object assetsProvider) throws IOException { - boolean system = ((int) flags & PROPERTY_SYSTEM) == PROPERTY_SYSTEM; - boolean overlay = ((int) flags & PROPERTY_OVERLAY) == PROPERTY_OVERLAY; - boolean forceSharedLib = ((int) flags & PROPERTY_DYNAMIC) == PROPERTY_DYNAMIC; - return nativeLoad((String) javaPath, system, forceSharedLib, overlay); + int format, + String javaPath, + int flags, + @ClassName(value = "android.content.res.loader.AssetsProvider") Object assetsProvider) + throws IOException { + boolean system = (flags & PROPERTY_SYSTEM) == PROPERTY_SYSTEM; + boolean overlay = (flags & PROPERTY_OVERLAY) == PROPERTY_OVERLAY; + boolean forceSharedLib = (flags & PROPERTY_DYNAMIC) == PROPERTY_DYNAMIC; + return nativeLoad(javaPath, system, forceSharedLib, overlay); } // static jlong NativeLoadFromFd(JNIEnv* env, jclass /*clazz*/, jobject file_descriptor, diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAudioManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAudioManager.java index 1aa553fa9c4..4027f98de45 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAudioManager.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAudioManager.java @@ -42,6 +42,7 @@ import java.util.List; import java.util.Map; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.ClassName; import org.robolectric.annotation.HiddenApi; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; @@ -51,7 +52,7 @@ import org.robolectric.util.reflector.ForType; @SuppressWarnings({"UnusedDeclaration"}) -@Implements(value = AudioManager.class, looseSignatures = true) +@Implements(value = AudioManager.class) public class ShadowAudioManager { @RealObject AudioManager realAudioManager; @@ -884,7 +885,8 @@ public AudioRecordingConfiguration createActiveRecordingConfiguration( @HiddenApi @Implementation(minSdk = P) @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) - protected int registerAudioPolicy(@NonNull Object audioPolicy) { + protected int registerAudioPolicy( + @NonNull @ClassName(value = "android.media.audiopolicy.AudioPolicy") Object audioPolicy) { Preconditions.checkNotNull(audioPolicy, "Illegal null AudioPolicy argument"); AudioPolicy policy = (AudioPolicy) audioPolicy; String id = getIdForAudioPolicy(audioPolicy); @@ -898,7 +900,8 @@ protected int registerAudioPolicy(@NonNull Object audioPolicy) { @HiddenApi @Implementation(minSdk = Q) - protected void unregisterAudioPolicy(@NonNull Object audioPolicy) { + protected void unregisterAudioPolicy( + @NonNull @ClassName(value = "android.media.audiopolicy.AudioPolicy") Object audioPolicy) { Preconditions.checkNotNull(audioPolicy, "Illegal null AudioPolicy argument"); AudioPolicy policy = (AudioPolicy) audioPolicy; registeredAudioPolicies.remove(getIdForAudioPolicy(policy)); diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAudioTrack.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAudioTrack.java index ecf5f16ea25..54bd428ce91 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAudioTrack.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowAudioTrack.java @@ -44,7 +44,7 @@ * other methods are expected run through the real class. The two {@link WriteMode} are treated the * same. */ -@Implements(value = AudioTrack.class, looseSignatures = true) +@Implements(value = AudioTrack.class) public class ShadowAudioTrack { /** diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowBluetoothAdapter.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowBluetoothAdapter.java index e9d7e4f7bb9..03243073141 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowBluetoothAdapter.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowBluetoothAdapter.java @@ -48,6 +48,7 @@ import java.util.concurrent.ConcurrentMap; import javax.annotation.Nullable; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.ClassName; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.annotation.RealObject; @@ -60,7 +61,7 @@ import org.robolectric.util.reflector.Static; @SuppressWarnings({"UnusedDeclaration"}) -@Implements(value = BluetoothAdapter.class, looseSignatures = true) +@Implements(value = BluetoothAdapter.class) public class ShadowBluetoothAdapter { @RealObject private BluetoothAdapter realAdapter; @@ -131,9 +132,9 @@ protected static BluetoothAdapter getDefaultAdapter() { return reflector(BluetoothAdapterReflector.class).getDefaultAdapter(); } - /** Requires LooseSignatures because of {@link AttributionSource} parameter */ @Implementation(minSdk = VERSION_CODES.TIRAMISU) - protected static Object createAdapter(Object attributionSource) { + protected static Object createAdapter( + @ClassName(value = "android.content.AttributionSource") Object attributionSource) { IBluetoothManager service = ReflectionHelpers.createDelegatingProxy( IBluetoothManager.class, new BluetoothManagerDelegate()); @@ -357,10 +358,7 @@ protected boolean setName(String name) { return true; } - /** - * Needs looseSignatures because in Android T the return value of this method was changed from - * bool to int. - */ + /** T return value changed from {@code int} to {@link Duration} starting in T. */ @Implementation protected Object setScanMode(int scanMode) { boolean result = true; @@ -396,10 +394,7 @@ protected int getScanMode() { return scanMode; } - /** - * Needs looseSignatures because the return value changed from {@code int} to {@link Duration} - * starting in T. - */ + /** In Android T the return value of this method was changed from bool to int. */ @Implementation protected Object getDiscoverableTimeout() { if (RuntimeEnvironment.getApiLevel() <= S_V2) { diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayManager.java index 776f501c304..83b72c9f795 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayManager.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowDisplayManager.java @@ -28,6 +28,7 @@ import org.robolectric.RuntimeEnvironment; import org.robolectric.android.Bootstrap; import org.robolectric.android.internal.DisplayConfig; +import org.robolectric.annotation.ClassName; import org.robolectric.annotation.HiddenApi; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; @@ -43,7 +44,7 @@ * For tests, display properties may be changed and devices may be added or removed * programmatically. */ -@Implements(value = DisplayManager.class, minSdk = JELLY_BEAN_MR1, looseSignatures = true) +@Implements(value = DisplayManager.class, minSdk = JELLY_BEAN_MR1) public class ShadowDisplayManager { @RealObject private DisplayManager realDisplayManager; @@ -316,14 +317,17 @@ public void setSaturationLevel(float level) { @Implementation(minSdk = P) @HiddenApi - protected void setBrightnessConfiguration(Object config) { + protected void setBrightnessConfiguration( + @ClassName(value = "android.hardware.display.BrightnessConfiguration") Object config) { setBrightnessConfigurationForUser(config, 0, context.getPackageName()); } @Implementation(minSdk = P) @HiddenApi protected void setBrightnessConfigurationForUser( - Object config, Object userId, Object packageName) { + @ClassName(value = "android.hardware.display.BrightnessConfiguration") Object config, + int userId, + String packageName) { getShadowDisplayManagerGlobal().setBrightnessConfigurationForUser(config, userId, packageName); } diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowHardwareRenderer.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowHardwareRenderer.java index 7b86dc2f487..f37e38f8761 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowHardwareRenderer.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowHardwareRenderer.java @@ -40,6 +40,7 @@ protected static long nCreateProxy( // need to use loose signatures here to account for signature changes @Implementation(minSdk = S) protected static long nCreateProxy(Object translucent, Object rootRenderNode) { + // TODO Find an approach to support the same method with disconnected range return nCreateProxy((boolean) translucent, (long) rootRenderNode); } diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowImsMmTelManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowImsMmTelManager.java index 8d447ed7c6f..456aeb89aa2 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowImsMmTelManager.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowImsMmTelManager.java @@ -41,7 +41,6 @@ @Implements( value = ImsMmTelManager.class, minSdk = VERSION_CODES.Q, - looseSignatures = true, isInAndroidSdk = false) @SystemApi public class ShadowImsMmTelManager { diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowInstrumentation.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowInstrumentation.java index c66b0622d98..1f59ec167dd 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowInstrumentation.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowInstrumentation.java @@ -75,7 +75,7 @@ import org.robolectric.util.reflector.ForType; import org.robolectric.util.reflector.WithType; -@Implements(value = Instrumentation.class, looseSignatures = true) +@Implements(value = Instrumentation.class) public class ShadowInstrumentation { @RealObject private Instrumentation realObject; diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPaint.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPaint.java index d56a7d7e5e9..97d5ff3ab7c 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPaint.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPaint.java @@ -23,6 +23,7 @@ import android.graphics.RectF; import android.graphics.Shader; import android.graphics.Typeface; +import org.robolectric.annotation.ClassName; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.annotation.RealObject; @@ -34,7 +35,7 @@ import org.robolectric.versioning.AndroidVersions.V; @SuppressWarnings({"UnusedDeclaration"}) -@Implements(value = Paint.class, looseSignatures = true) +@Implements(value = Paint.class) public class ShadowPaint { private int color; @@ -534,8 +535,11 @@ protected static int nGetFontMetricsInt( } @Implementation(minSdk = N, maxSdk = N_MR1) - protected int nGetFontMetricsInt(Object nativePaint, Object nativeTypeface, Object fmi) { - return nGetFontMetricsInt((long) nativePaint, (FontMetricsInt) fmi); + protected int nGetFontMetricsInt( + long nativePaint, + long nativeTypeface, + @ClassName(value = "android.graphics.Paint#FontMetricsInt") Object fmi) { + return nGetFontMetricsInt(nativePaint, (FontMetricsInt) fmi); } @Implementation(maxSdk = M) diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPhoneWindow.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPhoneWindow.java index 9411ff1c82e..0ebfd2663d5 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPhoneWindow.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPhoneWindow.java @@ -13,11 +13,11 @@ import org.robolectric.annotation.RealObject; import org.robolectric.util.reflector.ForType; -/** - * Shadow for PhoneWindow for APIs 23+ - */ -@Implements(className = "com.android.internal.policy.PhoneWindow", isInAndroidSdk = false, - minSdk = M, looseSignatures = true) +/** Shadow for PhoneWindow for APIs 23+ */ +@Implements( + className = "com.android.internal.policy.PhoneWindow", + isInAndroidSdk = false, + minSdk = M) public class ShadowPhoneWindow extends ShadowWindow { protected @RealObject Window realWindow; protected boolean decorFitsSystemWindows = true; diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPhoneWindowFor22.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPhoneWindowFor22.java index f39f66e693b..6fae3dd26c5 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPhoneWindowFor22.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPhoneWindowFor22.java @@ -8,11 +8,11 @@ import org.robolectric.annotation.Implements; import org.robolectric.util.reflector.ForType; -/** - * Shadow for the API 16-22 PhoneWindow.li - */ -@Implements(className = "com.android.internal.policy.impl.PhoneWindow", maxSdk = LOLLIPOP_MR1, - looseSignatures = true, isInAndroidSdk = false) +/** Shadow for the API 16-22 PhoneWindow.li */ +@Implements( + className = "com.android.internal.policy.impl.PhoneWindow", + maxSdk = LOLLIPOP_MR1, + isInAndroidSdk = false) public class ShadowPhoneWindowFor22 extends ShadowPhoneWindow { @Override @Implementation(maxSdk = LOLLIPOP_MR1) diff --git a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPowerManager.java b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPowerManager.java index 9bce4cca6b4..896d43ace3f 100644 --- a/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPowerManager.java +++ b/shadows/framework/src/main/java/org/robolectric/shadows/ShadowPowerManager.java @@ -44,6 +44,7 @@ import java.util.Optional; import java.util.Set; import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.ClassName; import org.robolectric.annotation.HiddenApi; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; @@ -54,7 +55,7 @@ import org.robolectric.util.reflector.ForType; /** Shadow of PowerManager */ -@Implements(value = PowerManager.class, looseSignatures = true) +@Implements(value = PowerManager.class) public class ShadowPowerManager { @RealObject private PowerManager realPowerManager; @@ -220,7 +221,9 @@ protected int getCurrentThermalStatus() { /** This function adds a listener for thermal status change. */ @Implementation(minSdk = Q) - protected void addThermalStatusListener(Object listener) { + protected void addThermalStatusListener( + @ClassName(value = "android.os.PowerManager#OnThermalStatusChangedListener") + Object listener) { checkState( listener instanceof PowerManager.OnThermalStatusChangedListener, "Listener must implement PowerManager.OnThermalStatusChangedListener"); @@ -234,7 +237,9 @@ public ImmutableSet