|
|
@ -10,6 +10,7 @@
|
|
|
|
package org.mozilla.fenix.perf
|
|
|
|
package org.mozilla.fenix.perf
|
|
|
|
|
|
|
|
|
|
|
|
import android.os.Build
|
|
|
|
import android.os.Build
|
|
|
|
|
|
|
|
import android.os.Handler
|
|
|
|
import android.os.Looper
|
|
|
|
import android.os.Looper
|
|
|
|
import android.os.StrictMode
|
|
|
|
import android.os.StrictMode
|
|
|
|
import androidx.annotation.VisibleForTesting
|
|
|
|
import androidx.annotation.VisibleForTesting
|
|
|
@ -21,8 +22,11 @@ import org.mozilla.fenix.Config
|
|
|
|
import org.mozilla.fenix.components.Components
|
|
|
|
import org.mozilla.fenix.components.Components
|
|
|
|
import org.mozilla.fenix.utils.ManufacturerCodes
|
|
|
|
import org.mozilla.fenix.utils.ManufacturerCodes
|
|
|
|
import org.mozilla.fenix.utils.Mockable
|
|
|
|
import org.mozilla.fenix.utils.Mockable
|
|
|
|
|
|
|
|
import java.util.concurrent.Executors
|
|
|
|
import java.util.concurrent.atomic.AtomicLong
|
|
|
|
import java.util.concurrent.atomic.AtomicLong
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private const val DELAY_TO_REMOVE_STRICT_MODE_MILLIS = 1000L
|
|
|
|
|
|
|
|
|
|
|
|
private val logger = Performance.logger
|
|
|
|
private val logger = Performance.logger
|
|
|
|
private val mainLooper = Looper.getMainLooper()
|
|
|
|
private val mainLooper = Looper.getMainLooper()
|
|
|
|
|
|
|
|
|
|
|
@ -64,7 +68,7 @@ class StrictModeManager(
|
|
|
|
.detectAll()
|
|
|
|
.detectAll()
|
|
|
|
.penaltyLog()
|
|
|
|
.penaltyLog()
|
|
|
|
if (setPenaltyDeath && Build.MANUFACTURER !in strictModeExceptionList) {
|
|
|
|
if (setPenaltyDeath && Build.MANUFACTURER !in strictModeExceptionList) {
|
|
|
|
threadPolicy.penaltyDeath()
|
|
|
|
threadPolicy.penaltyDeathWithIgnores()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
StrictMode.setThreadPolicy(threadPolicy.build())
|
|
|
|
StrictMode.setThreadPolicy(threadPolicy.build())
|
|
|
|
|
|
|
|
|
|
|
@ -94,10 +98,18 @@ class StrictModeManager(
|
|
|
|
fragmentManager.registerFragmentLifecycleCallbacks(object :
|
|
|
|
fragmentManager.registerFragmentLifecycleCallbacks(object :
|
|
|
|
FragmentManager.FragmentLifecycleCallbacks() {
|
|
|
|
FragmentManager.FragmentLifecycleCallbacks() {
|
|
|
|
override fun onFragmentResumed(fm: FragmentManager, f: Fragment) {
|
|
|
|
override fun onFragmentResumed(fm: FragmentManager, f: Fragment) {
|
|
|
|
enableStrictMode(setPenaltyDeath = false)
|
|
|
|
|
|
|
|
fm.unregisterFragmentLifecycleCallbacks(this)
|
|
|
|
fm.unregisterFragmentLifecycleCallbacks(this)
|
|
|
|
}
|
|
|
|
|
|
|
|
}, false)
|
|
|
|
// If we don't post when using penaltyListener on P+, the violation listener is never
|
|
|
|
|
|
|
|
// called. My best guess is that, unlike penaltyDeath, the violations are not
|
|
|
|
|
|
|
|
// delivered instantaneously so posting gives time for the violation listeners to
|
|
|
|
|
|
|
|
// run before they are removed here. This may be a race so we give the listeners a
|
|
|
|
|
|
|
|
// little extra time to run too though this way we may accidentally trigger
|
|
|
|
|
|
|
|
// violations for non-startup, which we haven't planned to do yet.
|
|
|
|
|
|
|
|
Handler(mainLooper).postDelayed({
|
|
|
|
|
|
|
|
enableStrictMode(setPenaltyDeath = false)
|
|
|
|
|
|
|
|
}, DELAY_TO_REMOVE_STRICT_MODE_MILLIS)
|
|
|
|
|
|
|
|
} }, false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
@ -145,3 +157,18 @@ class StrictModeManager(
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
private val strictModeExceptionList = setOf(ManufacturerCodes.HUAWEI, ManufacturerCodes.ONE_PLUS)
|
|
|
|
private val strictModeExceptionList = setOf(ManufacturerCodes.HUAWEI, ManufacturerCodes.ONE_PLUS)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private fun StrictMode.ThreadPolicy.Builder.penaltyDeathWithIgnores(): StrictMode.ThreadPolicy.Builder {
|
|
|
|
|
|
|
|
// If we want to apply ignores based on stack trace contents to APIs below P, we can use this methodology:
|
|
|
|
|
|
|
|
// https://medium.com/@tokudu/how-to-whitelist-strictmode-violations-on-android-based-on-stacktrace-eb0018e909aa
|
|
|
|
|
|
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
|
|
|
|
|
|
|
penaltyDeath()
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
// Ideally, we'd use a shared thread pool but we don't have any on the system currently
|
|
|
|
|
|
|
|
// (all shared ones are coroutine dispatchers).
|
|
|
|
|
|
|
|
val executor = Executors.newSingleThreadExecutor()
|
|
|
|
|
|
|
|
penaltyListener(executor, ThreadPenaltyDeathWithIgnoresListener())
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return this
|
|
|
|
|
|
|
|
}
|
|
|
|