leakcanary
leakcanary copied to clipboard
Google play pre launched report warns non SDK api used violation
Description
Google play pre launched report warns of non SDK api used violation by LeakCanary.
Steps to Reproduce
- Add the following dependency
com.squareup.leakcanary:leakcanary-object-watcher-android:2.7 - Publish a release build to the Play store
Expected behavior: No NonSdkApiUsedViolation warnings in the reports from LeakCanary
Version Information
- LeakCanary version: 2.7
Additional Information
Google play pre launch reports: Report 1
StrictMode policy violation: android.os.strictmode.NonSdkApiUsedViolation: Landroid/app/ActivityThread;->mServices:Landroid/util/ArrayMap;
at android.os.StrictMode.lambda$static$1(StrictMode.java:407)
at android.os.-$$Lambda$StrictMode$lu9ekkHJ2HMz0jd3F8K8MnhenxQ.accept(Unknown Source:2)
at java.lang.Class.getDeclaredField(Native Method)
at leakcanary.ServiceWatcher$activityThreadServices$2.invoke(ServiceWatcher.kt:3)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:5)
at leakcanary.ServiceWatcher$install$3$2.handleMessage(ServiceWatcher.kt:4)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:237)
at android.app.ActivityThread.main(ActivityThread.java:8016)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:496)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1076)
Report 2
StrictMode policy violation: android.os.strictmode.NonSdkApiUsedViolation: Landroid/app/ActivityThread;->mServices:Landroid/util/ArrayMap;
at android.os.StrictMode.lambda$static$1(StrictMode.java:416)
at android.os.-$$Lambda$StrictMode$lu9ekkHJ2HMz0jd3F8K8MnhenxQ.accept(Unknown Source:2)
at java.lang.Class.getDeclaredField(Native Method)
at leakcanary.ServiceWatcher$activityThreadServices$2.invoke(ServiceWatcher.kt:3)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:5)
at leakcanary.ServiceWatcher$install$3$2.handleMessage(ServiceWatcher.kt:4)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7664)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Report 3
StrictMode policy violation: android.os.strictmode.NonSdkApiUsedViolation: Landroid/app/ActivityThread;->mServices:Landroid/util/ArrayMap;
at android.os.StrictMode.lambda$static$1(StrictMode.java:428)
at android.os.-$$Lambda$StrictMode$lu9ekkHJ2HMz0jd3F8K8MnhenxQ.accept(Unknown Source:2)
at java.lang.Class.getDeclaredField(Native Method)
at leakcanary.ServiceWatcher$activityThreadServices$2.invoke(ServiceWatcher.kt:3)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:5)
at leakcanary.ServiceWatcher$install$3$2.handleMessage(ServiceWatcher.kt:4)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6718)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Until Google provides the APIs we need to do this, there's really no way around it. This strict mode thing is just sad. You can always disable the ServiceWatcher if you really care.
We could potentially provide utilities to help ignore LeakCanary strict mode violations. Not sure about any auto install though.
Something inspired from this:
private fun initializeStrictMode() {
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder(StrictMode.getVmPolicy())
.run {
if (Build.VERSION.SDK_INT >= 28) {
detectNonSdkApiUsage()
.penaltyListener(
{
it.run()
},
{ violation ->
handleViolation(violation)
}
)
} else {
this
}
}
.build()
)
}
@RequiresApi(28)
private fun handleViolation(violation: Violation) {
when (violation) {
is NonSdkApiUsedViolation -> handleNonSdkApiUsedViolation(violation)
}
}
// non-sdk API calls in code that we have control of.
private val knownViolations = setOf(
// Called in com.squareup.shark.config.ImmCurRootViewFix on API 28 only, where it was on the
// greylist. Later it was moved on the black list, but that's not important anymore.
"Landroid/view/inputmethod/InputMethodManager;->mCurRootView:Landroid/view/View;",
"Ljava/lang/Throwable;->detailMessage:Ljava/lang/String;",
)
private fun handleNonSdkApiUsedViolation(violation: NonSdkApiUsedViolation) {
// Ignore the known violations.
if (violation.message in knownViolations) return
// Exceptions look something like that:
//
// Caused by: android.os.strictmode.NonSdkApiUsedViolation: Landroid/view/View;->mKeyedTags:Landroid/util/SparseArray;
// at android.os.StrictMode.lambda$static$1(StrictMode.java:416)
// at android.os.-$$Lambda$StrictMode$lu9ekkHJ2HMz0jd3F8K8MnhenxQ.accept(Unknown Source:2)
// at java.lang.Class.getDeclaredField(Native Method)
// at com.facebook.flipper.plugins.inspector.descriptors.ViewDescriptor.<clinit>(ViewDescriptor.java:63)
//
// This code gives us the important line after the reflection call.
val offendingCaller = violation.stackTrace.first {
!it.className.startsWith("android.os") && !it.className.startsWith("java.lang.Class")
}
// These callers use elements from the greylist. Unfortunately, the violation object doesn't
// give us any hint about what kind of violation it is. We can safely ignore greylist filters and
// filter them here.
//
// Note that these callers are only third party dependencies. Our own violations are listed in
// the 'knownViolations' collection.
//
// When we upgrade the target SDK, we should check them all again.
when {
// This is fiiiine
offendingCaller.className.startsWith("papa.") -> return
offendingCaller.className.startsWith("curtains.") -> return
offendingCaller.className.startsWith("leakcanary.") -> return
offendingCaller.className.startsWith("radiography.") -> return
// Google probably knows what they're doing.
offendingCaller.className.startsWith("androidx.") -> return
// etc etc
}
throw violation
}