Skip to content

Commit a15f128

Browse files
committed
Issue mozilla-mobile#5818: Try to migrate default search engine.
1 parent 2b2ed9c commit a15f128

File tree

8 files changed

+446
-2
lines changed

8 files changed

+446
-2
lines changed

components/support/migration/build.gradle

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,11 @@ tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all {
6464

6565
dependencies {
6666
implementation project(':concept-engine')
67-
implementation project(':browser-session')
6867

68+
implementation project(':browser-search')
69+
implementation project(':browser-session')
6970
implementation project(':browser-storage-sync')
71+
7072
implementation project(':service-firefox-accounts')
7173
implementation project(':service-sync-logins')
7274
implementation project(':service-glean')

components/support/migration/docs/metrics.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,15 @@ The following metrics are added to the ping:
5252
| migration.logins.failure_reason |[counter](https://mozilla.github.io/glean/book/user/metrics/counter.html) |Why this migration failed. See codes in TelemetryHelpers.kt |[1](https://github.com/mozilla-mobile/android-components/pull/5483#issuecomment-573971458)||2020-10-01 |
5353
| migration.logins.success_reason |[counter](https://mozilla.github.io/glean/book/user/metrics/counter.html) |Why this migration succeeded. See codes in TelemetryHelpers.kt |[1](https://github.com/mozilla-mobile/android-components/pull/5483#issuecomment-573971458)||2020-10-01 |
5454
| migration.logins.unsupported_db_version |[counter](https://mozilla.github.io/glean/book/user/metrics/counter.html) |If we couldn't migrate due to an unsupported db version, what was it? |[1](https://github.com/mozilla-mobile/android-components/pull/5483#issuecomment-573971458)||2020-10-01 |
55-
| migration.migration_versions |[labeled_string](https://mozilla.github.io/glean/book/user/metrics/labeled_strings.html) |Versions of the migrations which were executed. |[1](https://github.com/mozilla-mobile/android-components/pull/5483#issuecomment-573971458)|<ul><li>history</li><li>bookmarks</li><li>logins</li><li>open_tabs</li><li>fxa</li><li>gecko</li><li>settings</li><li>telemetry_identifiers</li><li>addons</li></ul>|2020-10-01 |
55+
| migration.migration_versions |[labeled_string](https://mozilla.github.io/glean/book/user/metrics/labeled_strings.html) |Versions of the migrations which were executed. |[1](https://github.com/mozilla-mobile/android-components/pull/5483#issuecomment-573971458)|<ul><li>history</li><li>bookmarks</li><li>logins</li><li>open_tabs</li><li>fxa</li><li>gecko</li><li>settings</li><li>telemetry_identifiers</li><li>addons</li><li>search</li></ul>|2020-10-01 |
5656
| migration.open_tabs.any_failures |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Did this migration encounter any failures (exceptions)? |[1](https://github.com/mozilla-mobile/android-components/pull/5483#issuecomment-573971458)||2020-10-01 |
5757
| migration.open_tabs.detected |[counter](https://mozilla.github.io/glean/book/user/metrics/counter.html) |Number of detected open tabs. |[1](https://github.com/mozilla-mobile/android-components/pull/5483#issuecomment-573971458)||2020-10-01 |
5858
| migration.open_tabs.failure_reason |[counter](https://mozilla.github.io/glean/book/user/metrics/counter.html) |Why this migration failed. See codes in TelemetryHelpers.kt |[1](https://github.com/mozilla-mobile/android-components/pull/5655#pullrequestreview-346359792)||2020-10-01 |
5959
| migration.open_tabs.migrated |[counter](https://mozilla.github.io/glean/book/user/metrics/counter.html) |Number of migrated open tabs. |[1](https://github.com/mozilla-mobile/android-components/pull/5483#issuecomment-573971458)||2020-10-01 |
6060
| migration.open_tabs.success_reason |[counter](https://mozilla.github.io/glean/book/user/metrics/counter.html) |Why this migration succeeded. See codes in TelemetryHelpers.kt |[1](https://github.com/mozilla-mobile/android-components/pull/5655#pullrequestreview-346359792)||2020-10-01 |
61+
| migration.search.any_failures |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Did this migration encounter any failures (exceptions)? |[1](TODO)||2020-10-01 |
62+
| migration.search.failure_reason |[counter](https://mozilla.github.io/glean/book/user/metrics/counter.html) |Why this migration failed. See codes in TelemetryHelpers.kt |[1](TODO)||2020-10-01 |
63+
| migration.search.success_reason |[counter](https://mozilla.github.io/glean/book/user/metrics/counter.html) |Why this migration succeeded. See codes in TelemetryHelpers.kt |[1](TODO)||2020-10-01 |
6164
| migration.settings.any_failures |[boolean](https://mozilla.github.io/glean/book/user/metrics/boolean.html) |Did this migration encounter any failures (exceptions)? |[1](https://github.com/mozilla-mobile/android-components/pull/5483#issuecomment-573971458)||2020-10-01 |
6265
| migration.settings.failure_reason |[counter](https://mozilla.github.io/glean/book/user/metrics/counter.html) |Why this migration failed. See codes in TelemetryHelpers.kt |[1](https://github.com/mozilla-mobile/android-components/pull/5483#issuecomment-573971458)||2020-10-01 |
6366
| migration.settings.success_reason |[counter](https://mozilla.github.io/glean/book/user/metrics/counter.html) |Why this migration succeeded. See codes in TelemetryHelpers.kt |[1](https://github.com/mozilla-mobile/android-components/pull/5483#issuecomment-573971458)||2020-10-01 |

components/support/migration/metrics.yaml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ migration:
1919
- settings
2020
- telemetry_identifiers
2121
- addons
22+
- search
2223
send_in_pings:
2324
- migration
2425
bugs:
@@ -745,3 +746,47 @@ migration.addons:
745746
746747
expires: 2020-10-01
747748
lifetime: ping
749+
750+
migration.search:
751+
any_failures:
752+
type: boolean
753+
description: >
754+
Did this migration encounter any failures (exceptions)?
755+
send_in_pings:
756+
- migration
757+
bugs:
758+
- https://github.com/mozilla-mobile/android-components/issues/5818
759+
data_reviews:
760+
- https://github.com/mozilla-mobile/android-components/pull/5819#pullrequestreview-353890362
761+
notification_emails:
762+
763+
expires: 2020-10-01
764+
lifetime: ping
765+
failure_reason:
766+
type: counter
767+
description: >
768+
Why this migration failed. See codes in TelemetryHelpers.kt
769+
send_in_pings:
770+
- migration
771+
bugs:
772+
- https://github.com/mozilla-mobile/android-components/issues/5818
773+
data_reviews:
774+
- https://github.com/mozilla-mobile/android-components/pull/5819#pullrequestreview-353890362
775+
notification_emails:
776+
777+
expires: 2020-10-01
778+
lifetime: ping
779+
success_reason:
780+
type: counter
781+
description: >
782+
Why this migration succeeded. See codes in TelemetryHelpers.kt
783+
send_in_pings:
784+
- migration
785+
bugs:
786+
- https://github.com/mozilla-mobile/android-components/issues/5818
787+
data_reviews:
788+
- https://github.com/mozilla-mobile/android-components/pull/5819#pullrequestreview-353890362
789+
notification_emails:
790+
791+
expires: 2020-10-01
792+
lifetime: ping

components/support/migration/src/main/java/mozilla/components/support/migration/FennecMigrator.kt

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import kotlinx.coroutines.asCoroutineDispatcher
1616
import kotlinx.coroutines.async
1717
import kotlinx.coroutines.runBlocking
1818
import kotlinx.coroutines.withContext
19+
import mozilla.components.browser.search.SearchEngineManager
1920
import mozilla.components.browser.session.SessionManager
2021
import mozilla.components.browser.storage.sync.PlacesBookmarksStorage
2122
import mozilla.components.browser.storage.sync.PlacesHistoryStorage
@@ -40,8 +41,10 @@ import mozilla.components.support.migration.GleanMetrics.MigrationHistory
4041
import mozilla.components.support.migration.GleanMetrics.MigrationLogins
4142
import mozilla.components.support.migration.GleanMetrics.MigrationTelemetryIdentifiers
4243
import mozilla.components.support.migration.GleanMetrics.MigrationOpenTabs
44+
import mozilla.components.support.migration.GleanMetrics.MigrationSearch
4345
import mozilla.components.support.migration.GleanMetrics.MigrationSettings
4446
import java.io.File
47+
import java.lang.AssertionError
4548
import java.lang.Exception
4649
import java.util.Date
4750
import java.util.UUID
@@ -97,6 +100,11 @@ sealed class Migration(val currentVersion: Int) {
97100
* Migrates Fennec's telemetry identifiers.
98101
*/
99102
object TelemetryIdentifiers : Migration(currentVersion = 1)
103+
104+
/**
105+
* Migrates Fennec's default search engine.
106+
*/
107+
object SearchEngine : Migration(currentVersion = 1)
100108
}
101109

102110
/**
@@ -167,6 +175,12 @@ sealed class FennecMigratorException(cause: Exception) : Exception(cause) {
167175
* @param cause Original exception which caused the problem.
168176
*/
169177
class TelemetryIdentifierException(cause: Exception) : FennecMigratorException(cause)
178+
179+
/**
180+
* Unexpected exception while migrating the default search engine.
181+
* @param cause Original exception which caused the problem.
182+
*/
183+
class MigrateSearchEngineException(cause: Exception) : FennecMigratorException(cause)
170184
}
171185

172186
/**
@@ -188,6 +202,7 @@ class FennecMigrator private constructor(
188202
private val loginsStorage: AsyncLoginsStorage?,
189203
private val loginsStorageKey: String?,
190204
private val sessionManager: SessionManager?,
205+
private val searchEngineManager: SearchEngineManager?,
191206
private val accountManager: FxaAccountManager?,
192207
private val engine: Engine?,
193208
private val addonCollectionProvider: AddonCollectionProvider?,
@@ -209,6 +224,7 @@ class FennecMigrator private constructor(
209224
private var loginsStorage: AsyncLoginsStorage? = null
210225
private var loginsStorageKey: String? = null
211226
private var sessionManager: SessionManager? = null
227+
private var searchEngineManager: SearchEngineManager? = null
212228
private var accountManager: FxaAccountManager? = null
213229
private var engine: Engine? = null
214230
private var addonCollectionProvider: AddonCollectionProvider? = null
@@ -303,6 +319,22 @@ class FennecMigrator private constructor(
303319
return this
304320
}
305321

322+
/**
323+
* Enable default search engine migration.
324+
*
325+
* @param searchEngineManager An instance of [SearchEngineManager] used for restoring the
326+
* migrated default search engine.
327+
* @param version Version of the migration; defaults to the current version.
328+
*/
329+
fun migrateSearchEngine(
330+
searchEngineManager: SearchEngineManager,
331+
version: Int = Migration.SearchEngine.currentVersion
332+
): Builder {
333+
this.searchEngineManager = searchEngineManager
334+
migrations.add(VersionedMigration(Migration.SearchEngine, version))
335+
return this
336+
}
337+
306338
/**
307339
* Enable FxA state migration.
308340
*
@@ -366,6 +398,7 @@ class FennecMigrator private constructor(
366398
loginsStorage,
367399
loginsStorageKey,
368400
sessionManager,
401+
searchEngineManager,
369402
accountManager,
370403
engine,
371404
addonCollectionProvider,
@@ -536,6 +569,7 @@ class FennecMigrator private constructor(
536569
Migration.Settings -> migrateSharedPrefs()
537570
Migration.Addons -> migrateAddons()
538571
Migration.TelemetryIdentifiers -> migrateTelemetryIdentifiers()
572+
Migration.SearchEngine -> migrateSearchEngine()
539573
}
540574

541575
val migrationRun = when (migrationResult) {
@@ -555,6 +589,7 @@ class FennecMigrator private constructor(
555589
Migration.Settings -> MigrationSettings.anyFailures.set(true)
556590
Migration.Addons -> MigrationAddons.anyFailures.set(true)
557591
Migration.TelemetryIdentifiers -> MigrationTelemetryIdentifiers.anyFailures.set(true)
592+
Migration.SearchEngine -> MigrationSearch.anyFailures.set(true)
558593
}
559594
setFailure(versionedMigration.migration)
560595

@@ -959,6 +994,50 @@ class FennecMigrator private constructor(
959994
}
960995
}
961996

997+
@SuppressWarnings("TooGenericExceptionCaught")
998+
private suspend fun migrateSearchEngine(): Result<SearchEngineMigrationResult> {
999+
val manager = searchEngineManager
1000+
?: throw AssertionError("Migrating search engines without search engine manager set")
1001+
1002+
try {
1003+
val result = SearchEngineMigration.migrate(context, manager)
1004+
1005+
if (result is Result.Failure<SearchEngineMigrationResult>) {
1006+
val migrationFailureWrapper = result.throwables.first() as SearchEngineMigrationException
1007+
return when (val failure = migrationFailureWrapper.failure) {
1008+
is SearchEngineMigrationResult.Failure.NoDefault -> {
1009+
logger.error("Missing search engine default: $failure")
1010+
crashReporter.submitCaughtException(migrationFailureWrapper)
1011+
MigrationSearch.failureReason.add(FailureReasonTelemetryCodes.SEARCH_NO_DEFAULT.code)
1012+
result
1013+
}
1014+
1015+
is SearchEngineMigrationResult.Failure.NoMatch -> {
1016+
logger.error("Could not find matching search engine: $failure")
1017+
crashReporter.submitCaughtException(migrationFailureWrapper)
1018+
MigrationSearch.failureReason.add(FailureReasonTelemetryCodes.SEARCH_NO_MATCH.code)
1019+
result
1020+
}
1021+
}
1022+
}
1023+
1024+
val migrationSuccess = result as Result.Success<SearchEngineMigrationResult>
1025+
return when (migrationSuccess.value as SearchEngineMigrationResult.Success) {
1026+
is SearchEngineMigrationResult.Success.SearchEngineMigrated -> {
1027+
logger.debug("Migrated default search engine")
1028+
MigrationSearch.successReason.add(SuccessReasonTelemetryCodes.SEARCH_MIGRATED.code)
1029+
result
1030+
}
1031+
}
1032+
} catch (e: Exception) {
1033+
crashReporter.submitCaughtException(
1034+
FennecMigratorException.MigrateSearchEngineException(e)
1035+
)
1036+
MigrationSearch.failureReason.add(FailureReasonTelemetryCodes.SEARCH_EXCEPTION.code)
1037+
return Result.Failure(e)
1038+
}
1039+
}
1040+
9621041
@SuppressWarnings("TooGenericExceptionCaught", "NestedBlockDepth", "ComplexMethod")
9631042
private suspend fun migrateAddons(): Result<AddonMigrationResult> {
9641043
return try {

components/support/migration/src/main/java/mozilla/components/support/migration/MigrationResultsStore.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ internal class MigrationResultsStore(context: Context) : SharedPreferencesCache<
6363
Migration.Settings.javaClass.simpleName -> Migration.Settings
6464
Migration.Addons.javaClass.simpleName -> Migration.Addons
6565
Migration.TelemetryIdentifiers.javaClass.simpleName -> Migration.TelemetryIdentifiers
66+
Migration.SearchEngine.javaClass.simpleName -> Migration.SearchEngine
6667
else -> throw IllegalStateException("Unrecognized migration type: $migrationName")
6768
}
6869
result[migration] = MigrationRun(version = migrationVersion, success = migrationSuccess)
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4+
5+
package mozilla.components.support.migration
6+
7+
import android.content.Context
8+
import mozilla.components.browser.search.SearchEngineManager
9+
import mozilla.components.support.base.log.logger.Logger
10+
11+
private const val PREF_FENNEC_DEFAULT_ENGINE_KEY = "search.engines.defaultname"
12+
private const val PREF_FENIX_DEFAULT_ENGINE_KEY = "pref_key_search_engine"
13+
14+
/**
15+
* Implementation for migrating the default search engine from Fennec to Fenix.
16+
*
17+
* This is a "best effort" migration. We will recover the default search engine name from Fennec and
18+
* then try to find a matching search engine in the search engine manager for the current Fenix
19+
* installation. If we find a match we will update the default search engine to that; otherwise
20+
* the regular default search engine for this installation will be used.
21+
*/
22+
internal object SearchEngineMigration {
23+
private val logger = Logger("SearchEngineMigration")
24+
25+
/**
26+
* Tries to migrate the default search engine from Fennec to Fenix.
27+
*/
28+
suspend fun migrate(
29+
context: Context,
30+
searchEngineManager: SearchEngineManager
31+
): Result<SearchEngineMigrationResult> {
32+
val fennecSearchEngine = retrieveFennecDefaultSearchEngineName(context)
33+
if (fennecSearchEngine == null) {
34+
logger.debug("Could not locate Fennec search engine preference")
35+
return Result.Failure(
36+
SearchEngineMigrationException(SearchEngineMigrationResult.Failure.NoDefault))
37+
}
38+
39+
logger.debug("Found search engine from Fennec: $fennecSearchEngine")
40+
41+
return determineNewDefault(context, searchEngineManager, fennecSearchEngine)
42+
}
43+
44+
private suspend fun determineNewDefault(
45+
context: Context,
46+
searchEngineManager: SearchEngineManager,
47+
name: String
48+
): Result<SearchEngineMigrationResult> {
49+
val searchEngines = searchEngineManager.getSearchEnginesAsync(context)
50+
51+
logger.debug("Got ${searchEngines.size} search engines from SearchEngineManager.")
52+
53+
// We will try to find a search engine that has a matching name in the list of search engines
54+
// for this Fenix installation (which depends on locale/region). If we find a match then we
55+
// update Fenix to use this search engine. If there's no match then we leave it to Fenix to
56+
// pick a default search engine.
57+
searchEngines.forEach { engine ->
58+
logger.debug(" - Fennec: $name - Comparing with Fenix search engine: ${engine.name}")
59+
60+
if (engine.name.contains(name, ignoreCase = true)) {
61+
logger.debug("Setting new default: ${engine.name}")
62+
63+
val fenixPreferences = context.getSharedPreferences(
64+
FennecSettingsMigration.FENIX_SHARED_PREFS_NAME,
65+
Context.MODE_PRIVATE
66+
)
67+
68+
fenixPreferences.edit()
69+
.putString(PREF_FENIX_DEFAULT_ENGINE_KEY, engine.name)
70+
.apply()
71+
72+
searchEngineManager.defaultSearchEngine = engine
73+
74+
return Result.Success(SearchEngineMigrationResult.Success.SearchEngineMigrated)
75+
}
76+
}
77+
78+
logger.debug("Could not find matching search engine")
79+
return Result.Failure(SearchEngineMigrationException(
80+
SearchEngineMigrationResult.Failure.NoMatch
81+
))
82+
}
83+
84+
private fun retrieveFennecDefaultSearchEngineName(context: Context): String? {
85+
val fennecPreferences = context.getSharedPreferences(
86+
FennecSettingsMigration.FENNEC_APP_SHARED_PREFS_NAME,
87+
Context.MODE_PRIVATE
88+
)
89+
90+
return fennecPreferences.getString(PREF_FENNEC_DEFAULT_ENGINE_KEY, null)
91+
}
92+
}
93+
94+
/**
95+
* Result of the default search engine migration.
96+
*/
97+
internal sealed class SearchEngineMigrationResult {
98+
internal sealed class Success : SearchEngineMigrationResult() {
99+
internal object SearchEngineMigrated : Success()
100+
}
101+
102+
internal sealed class Failure : SearchEngineMigrationResult() {
103+
internal object NoDefault : SearchEngineMigrationResult.Failure()
104+
internal object NoMatch : SearchEngineMigrationResult.Failure()
105+
}
106+
}
107+
108+
/**
109+
* Wraps [SearchEngineMigrationResult] in an exception so that it can be returned via [Result.Failure].
110+
*/
111+
internal class SearchEngineMigrationException(
112+
val failure: SearchEngineMigrationResult.Failure
113+
) : Exception(failure.toString())

components/support/migration/src/main/java/mozilla/components/support/migration/TelemetryHelpers.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ internal fun Migration.telemetryIdentifier(): String {
1616
Migration.Settings -> "settings"
1717
Migration.Addons -> "addons"
1818
Migration.TelemetryIdentifiers -> "telemetry_identifiers"
19+
Migration.SearchEngine -> "search"
1920
}
2021
}
2122

@@ -64,6 +65,10 @@ internal enum class FailureReasonTelemetryCodes(val code: Int) {
6465

6566
TELEMETRY_IDENTIFIERS_MISSING_PROFILE(29),
6667
TELEMETRY_IDENTIFIERS_MIGRATE_EXCEPTION(30),
68+
69+
SEARCH_NO_DEFAULT(31),
70+
SEARCH_NO_MATCH(32),
71+
SEARCH_EXCEPTION(33)
6772
}
6873

6974
@SuppressWarnings("MagicNumber")
@@ -94,4 +99,5 @@ internal enum class SuccessReasonTelemetryCodes(val code: Int) {
9499
TELEMETRY_IDENTIFIERS_MIGRATED(16),
95100

96101
FXA_WILL_RETRY(17),
102+
SEARCH_MIGRATED(18)
97103
}

0 commit comments

Comments
 (0)