diff --git a/app/src/debug/java/org/mozilla/fenix/DebugFenixApplication.kt b/app/src/debug/java/org/mozilla/fenix/DebugFenixApplication.kt index 89571029c..abbb65793 100644 --- a/app/src/debug/java/org/mozilla/fenix/DebugFenixApplication.kt +++ b/app/src/debug/java/org/mozilla/fenix/DebugFenixApplication.kt @@ -4,17 +4,20 @@ package org.mozilla.fenix +import android.os.StrictMode import androidx.preference.PreferenceManager import leakcanary.AppWatcher import leakcanary.LeakCanary +import mozilla.components.support.ktx.android.os.resetAfter import org.mozilla.fenix.ext.getPreferenceKey class DebugFenixApplication : FenixApplication() { override fun setupLeakCanary() { - val isEnabled = PreferenceManager.getDefaultSharedPreferences(this) - .getBoolean(getPreferenceKey(R.string.pref_key_leakcanary), true) - + val isEnabled = StrictMode.allowThreadDiskReads().resetAfter { + PreferenceManager.getDefaultSharedPreferences(this) + .getBoolean(getPreferenceKey(R.string.pref_key_leakcanary), true) + } updateLeakCanaryState(isEnabled) } diff --git a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt index 92fb8cfd3..1090b4883 100644 --- a/app/src/main/java/org/mozilla/fenix/FenixApplication.kt +++ b/app/src/main/java/org/mozilla/fenix/FenixApplication.kt @@ -49,6 +49,8 @@ import org.mozilla.fenix.session.PerformanceActivityLifecycleCallbacks import org.mozilla.fenix.session.VisibilityLifecycleCallback import org.mozilla.fenix.utils.BrowsersCache import org.mozilla.fenix.utils.Settings +import mozilla.components.support.ktx.android.os.resetAfter +import org.mozilla.fenix.StrictModeManager.enableStrictMode /** *The main application class for Fenix. Records data to measure initialization performance. @@ -124,12 +126,13 @@ open class FenixApplication : LocaleAwareApplication() { val megazordSetup = setupMegazord() setDayNightTheme() - enableStrictMode() + enableStrictMode(true) warmBrowsersCache() // Make sure the engine is initialized and ready to use. - components.core.engine.warmUp() - + StrictMode.allowThreadDiskReads().resetAfter { + components.core.engine.warmUp() + } initializeWebExtensionSupport() // Just to make sure it is impossible for any application-services pieces @@ -336,28 +339,6 @@ open class FenixApplication : LocaleAwareApplication() { } } - private fun enableStrictMode() { - if (Config.channel.isDebug) { - StrictMode.setThreadPolicy( - StrictMode.ThreadPolicy.Builder() - .detectAll() - .penaltyLog() - .build() - ) - var builder = StrictMode.VmPolicy.Builder() - .detectLeakedSqlLiteObjects() - .detectLeakedClosableObjects() - .detectLeakedRegistrationObjects() - .detectActivityLeaks() - .detectFileUriExposure() - .penaltyLog() - if (SDK_INT >= Build.VERSION_CODES.O) builder = - builder.detectContentUriWithoutPermission() - if (SDK_INT >= Build.VERSION_CODES.P) builder = builder.detectNonSdkApiUsage() - StrictMode.setVmPolicy(builder.build()) - } - } - private fun initializeWebExtensionSupport() { try { GlobalAddonDependencyProvider.initialize( diff --git a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt index 07f717e16..efa76e346 100644 --- a/app/src/main/java/org/mozilla/fenix/HomeActivity.kt +++ b/app/src/main/java/org/mozilla/fenix/HomeActivity.kt @@ -7,6 +7,7 @@ package org.mozilla.fenix import android.content.Context import android.content.Intent import android.os.Bundle +import android.os.StrictMode import android.util.AttributeSet import android.view.View import android.view.WindowManager @@ -39,6 +40,7 @@ import mozilla.components.service.fxa.sync.SyncReason import mozilla.components.support.base.feature.UserInteractionHandler import mozilla.components.support.ktx.android.arch.lifecycle.addObservers import mozilla.components.support.ktx.android.content.share +import mozilla.components.support.ktx.android.os.resetAfter import mozilla.components.support.ktx.kotlin.isUrl import mozilla.components.support.ktx.kotlin.toNormalizedUrl import mozilla.components.support.locale.LocaleAwareAppCompatActivity @@ -122,7 +124,11 @@ open class HomeActivity : LocaleAwareAppCompatActivity() { } final override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) + StrictModeManager.changeStrictModePolicies(supportFragmentManager) + // There is disk read violations on some devices such as samsung and pixel for android 9/10 + StrictMode.allowThreadDiskReads().resetAfter { + super.onCreate(savedInstanceState) + } components.publicSuffixList.prefetch() @@ -428,6 +434,12 @@ open class HomeActivity : LocaleAwareAppCompatActivity() { browsingModeManager.mode = sessionMode } + override fun attachBaseContext(base: Context) { + StrictMode.allowThreadDiskReads().resetAfter { + super.attachBaseContext(base) + } + } + protected open fun createBrowsingModeManager(initialMode: BrowsingMode): BrowsingModeManager { return DefaultBrowsingModeManager(initialMode) { newMode -> themeManager.currentTheme = newMode diff --git a/app/src/main/java/org/mozilla/fenix/StrictModeManager.kt b/app/src/main/java/org/mozilla/fenix/StrictModeManager.kt new file mode 100644 index 000000000..143f46b4e --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/StrictModeManager.kt @@ -0,0 +1,64 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public
 +* License, v. 2.0. If a copy of the MPL was not distributed with this
 +* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix + +import android.os.Build +import android.os.StrictMode +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager + +/** + * Manages strict mode settings for the application. + */ +object StrictModeManager { + + /*** + * Enables strict mode for debug purposes. meant to be run only in the main process. + * @param setPenaltyDialog boolean value to decide setting the dialog box as a penalty. + */ + fun enableStrictMode(setPenaltyDialog: Boolean) { + if (Config.channel.isDebug) { + val threadPolicy = StrictMode.ThreadPolicy.Builder() + .detectAll() + .penaltyLog() + if (setPenaltyDialog) { + threadPolicy.penaltyDialog() + } + StrictMode.setThreadPolicy(threadPolicy.build()) + var builder = StrictMode.VmPolicy.Builder() + .detectLeakedSqlLiteObjects() + .detectLeakedClosableObjects() + .detectLeakedRegistrationObjects() + .detectActivityLeaks() + .detectFileUriExposure() + .penaltyLog() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) builder = + builder.detectContentUriWithoutPermission() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + if (setPenaltyDialog) { + builder.permitNonSdkApiUsage() + } else { + builder.detectNonSdkApiUsage() + } + } + StrictMode.setVmPolicy(builder.build()) + } + } + + /** + * Revert strict mode to disable penalty dialog. Tied to fragment lifecycle since strict mode + * needs to switch to penalty logs. Using the fragment life cycle allows decoupling from any + * specific fragment. + */ + fun changeStrictModePolicies(fragmentManager: FragmentManager) { + fragmentManager.registerFragmentLifecycleCallbacks(object : + FragmentManager.FragmentLifecycleCallbacks() { + override fun onFragmentResumed(fm: FragmentManager, f: Fragment) { + enableStrictMode(false) + fm.unregisterFragmentLifecycleCallbacks(this) + } + }, false) + } +} diff --git a/app/src/main/java/org/mozilla/fenix/components/AccountAbnormalities.kt b/app/src/main/java/org/mozilla/fenix/components/AccountAbnormalities.kt index bc040160f..761018b97 100644 --- a/app/src/main/java/org/mozilla/fenix/components/AccountAbnormalities.kt +++ b/app/src/main/java/org/mozilla/fenix/components/AccountAbnormalities.kt @@ -5,6 +5,7 @@ package org.mozilla.fenix.components import android.content.Context +import android.os.StrictMode import androidx.annotation.GuardedBy import androidx.annotation.VisibleForTesting import kotlinx.coroutines.CoroutineScope @@ -17,6 +18,7 @@ import mozilla.components.concept.sync.OAuthAccount import mozilla.components.lib.crash.CrashReporter import mozilla.components.service.fxa.manager.FxaAccountManager import mozilla.components.support.base.log.logger.Logger +import mozilla.components.support.ktx.android.os.resetAfter import kotlin.coroutines.CoroutineContext /** @@ -75,7 +77,9 @@ class AccountAbnormalities( private val logger = Logger("AccountAbnormalities") - private val prefs = context.getSharedPreferences(PREF_FXA_ABNORMALITIES, Context.MODE_PRIVATE) + private val prefs = StrictMode.allowThreadDiskReads().resetAfter { + context.getSharedPreferences(PREF_FXA_ABNORMALITIES, Context.MODE_PRIVATE) + } /** * Once [accountManager] is initialized, queries it to detect abnormal account states. diff --git a/app/src/main/java/org/mozilla/fenix/components/TabCollectionStorage.kt b/app/src/main/java/org/mozilla/fenix/components/TabCollectionStorage.kt index e0a6ddce7..7e2521ca4 100644 --- a/app/src/main/java/org/mozilla/fenix/components/TabCollectionStorage.kt +++ b/app/src/main/java/org/mozilla/fenix/components/TabCollectionStorage.kt @@ -5,6 +5,7 @@ package org.mozilla.fenix.components import android.content.Context +import android.os.StrictMode import androidx.lifecycle.LiveData import androidx.paging.DataSource import mozilla.components.browser.session.Session @@ -14,6 +15,7 @@ import mozilla.components.feature.tab.collections.TabCollection import mozilla.components.feature.tab.collections.TabCollectionStorage import mozilla.components.support.base.observer.Observable import mozilla.components.support.base.observer.ObserverRegistry +import mozilla.components.support.ktx.android.os.resetAfter import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.toShortUrl import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionViewHolder @@ -49,7 +51,9 @@ class TabCollectionStorage( var cachedTabCollections = listOf() private val collectionStorage by lazy { - TabCollectionStorage(context, sessionManager) + StrictMode.allowThreadDiskReads().resetAfter { + TabCollectionStorage(context, sessionManager) + } } fun createCollection(title: String, sessions: List) { diff --git a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt index 6f5ea51ba..3e2988e01 100644 --- a/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt @@ -10,6 +10,7 @@ import android.content.DialogInterface import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.ColorDrawable import android.os.Bundle +import android.os.StrictMode import android.view.Gravity import android.view.LayoutInflater import android.view.View @@ -59,6 +60,7 @@ import mozilla.components.concept.sync.OAuthAccount import mozilla.components.feature.tab.collections.TabCollection import mozilla.components.feature.top.sites.TopSite import mozilla.components.lib.state.ext.consumeFrom +import mozilla.components.support.ktx.android.os.resetAfter import mozilla.components.support.ktx.android.util.dpToPx import org.mozilla.fenix.BrowserDirection import org.mozilla.fenix.HomeActivity @@ -146,8 +148,10 @@ class HomeFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) postponeEnterTransition() - if (!onboarding.userHasBeenOnboarded()) { - requireComponents.analytics.metrics.track(Event.OpenedAppFirstRun) + StrictMode.allowThreadDiskReads().resetAfter { + if (!onboarding.userHasBeenOnboarded()) { + requireComponents.analytics.metrics.track(Event.OpenedAppFirstRun) + } } } @@ -172,7 +176,9 @@ class HomeFragment : Fragment() { collections = requireComponents.core.tabCollectionStorage.cachedTabCollections, expandedCollections = emptySet(), mode = currentMode.getCurrentMode(), - topSites = requireComponents.core.topSiteStorage.cachedTopSites, + topSites = StrictMode.allowThreadDiskReads().resetAfter { + requireComponents.core.topSiteStorage.cachedTopSites + }, tip = FenixTipManager(listOf(MigrationTipProvider(requireContext()))).getTip() ) ) diff --git a/app/src/main/java/org/mozilla/fenix/whatsnew/WhatsNew.kt b/app/src/main/java/org/mozilla/fenix/whatsnew/WhatsNew.kt index aad54640b..ea2950c79 100644 --- a/app/src/main/java/org/mozilla/fenix/whatsnew/WhatsNew.kt +++ b/app/src/main/java/org/mozilla/fenix/whatsnew/WhatsNew.kt @@ -5,6 +5,8 @@ package org.mozilla.fenix.whatsnew * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import android.content.Context +import android.os.StrictMode +import mozilla.components.support.ktx.android.os.resetAfter // This file is a modified port from Focus Android @@ -68,7 +70,9 @@ class WhatsNew private constructor(private val storage: WhatsNewStorage) { fun shouldHighlightWhatsNew(context: Context): Boolean { return shouldHighlightWhatsNew( ContextWhatsNewVersion(context), - SharedPreferenceWhatsNewStorage(context) + StrictMode.allowThreadDiskReads().resetAfter { + SharedPreferenceWhatsNewStorage(context) + } ) }