summaryrefslogtreecommitdiffstats
path: root/mobile
diff options
context:
space:
mode:
Diffstat (limited to 'mobile')
-rw-r--r--mobile/android/android-components/components/feature/webcompat/src/main/assets/extensions/webcompat/data/injections.js14
-rw-r--r--mobile/android/android-components/components/feature/webcompat/src/main/assets/extensions/webcompat/injections/css/bug1882040-disable-pull-to-refresh.css14
-rw-r--r--mobile/android/android-components/components/feature/webcompat/src/main/assets/extensions/webcompat/manifest.json2
-rw-r--r--mobile/android/fenix/app/metrics.yaml14
-rw-r--r--mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/metrics/FirstSessionPing.kt39
-rw-r--r--mobile/android/fenix/app/src/test/java/org/mozilla/fenix/components/metrics/FirstSessionPingTest.kt126
-rw-r--r--mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/LollipopAsyncCodec.java14
-rw-r--r--mobile/android/version.txt2
8 files changed, 222 insertions, 3 deletions
diff --git a/mobile/android/android-components/components/feature/webcompat/src/main/assets/extensions/webcompat/data/injections.js b/mobile/android/android-components/components/feature/webcompat/src/main/assets/extensions/webcompat/data/injections.js
index 87b1da747b..a188a40f8d 100644
--- a/mobile/android/android-components/components/feature/webcompat/src/main/assets/extensions/webcompat/data/injections.js
+++ b/mobile/android/android-components/components/feature/webcompat/src/main/assets/extensions/webcompat/data/injections.js
@@ -1056,6 +1056,20 @@ const AVAILABLE_INJECTIONS = [
],
},
},
+ {
+ id: "1882040",
+ platform: "android",
+ domain: "YouTube Shorts",
+ bug: "1882040",
+ contentScripts: {
+ matches: ["*://m.youtube.com/shorts/*"],
+ css: [
+ {
+ file: "injections/css/bug1882040-disable-pull-to-refresh.css",
+ },
+ ],
+ },
+ },
];
module.exports = AVAILABLE_INJECTIONS;
diff --git a/mobile/android/android-components/components/feature/webcompat/src/main/assets/extensions/webcompat/injections/css/bug1882040-disable-pull-to-refresh.css b/mobile/android/android-components/components/feature/webcompat/src/main/assets/extensions/webcompat/injections/css/bug1882040-disable-pull-to-refresh.css
new file mode 100644
index 0000000000..b075f96212
--- /dev/null
+++ b/mobile/android/android-components/components/feature/webcompat/src/main/assets/extensions/webcompat/injections/css/bug1882040-disable-pull-to-refresh.css
@@ -0,0 +1,14 @@
+/* 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/. */
+
+/**
+ * m.youtube.com/shorts - pull-to-refresh breaks scrolling
+ * Bug #1882040 - https://bugzilla.mozilla.org/show_bug.cgi?id=1882040
+ *
+ * Pull-to-refresh is breaking scrolling on the YouTube Shorts mobile page.
+ * The easiest work-around is to inject this CSS to disable it for now.
+ */
+html {
+ overscroll-behavior: contain;
+}
diff --git a/mobile/android/android-components/components/feature/webcompat/src/main/assets/extensions/webcompat/manifest.json b/mobile/android/android-components/components/feature/webcompat/src/main/assets/extensions/webcompat/manifest.json
index 6f6d519d7c..8353eee2f4 100644
--- a/mobile/android/android-components/components/feature/webcompat/src/main/assets/extensions/webcompat/manifest.json
+++ b/mobile/android/android-components/components/feature/webcompat/src/main/assets/extensions/webcompat/manifest.json
@@ -2,7 +2,7 @@
"manifest_version": 2,
"name": "Mozilla Android Components - Web Compatibility Interventions",
"description": "Urgent post-release fixes for web compatibility.",
- "version": "125.0.0",
+ "version": "125.2.0",
"browser_specific_settings": {
"gecko": {
"id": "webcompat@mozilla.org",
diff --git a/mobile/android/fenix/app/metrics.yaml b/mobile/android/fenix/app/metrics.yaml
index a620eb4d39..2b94cb5cd4 100644
--- a/mobile/android/fenix/app/metrics.yaml
+++ b/mobile/android/fenix/app/metrics.yaml
@@ -7731,6 +7731,20 @@ first_session:
tags:
- Performance
- Attribution
+ install_source:
+ type: string
+ lifetime: application
+ description: |
+ Used to identify the source the app was installed from.
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1898363
+ data_reviews:
+ - https://phabricator.services.mozilla.com/D212913
+ data_sensitivity:
+ - technical
+ notification_emails:
+ - android-probes@mozilla.com
+ expires: never
play_store_attribution:
install_referrer_response:
type: text
diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/metrics/FirstSessionPing.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/metrics/FirstSessionPing.kt
index 0666ab06e7..d281f03c06 100644
--- a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/metrics/FirstSessionPing.kt
+++ b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/metrics/FirstSessionPing.kt
@@ -4,8 +4,13 @@
package org.mozilla.fenix.components.metrics
+import android.annotation.SuppressLint
import android.content.Context
import android.content.SharedPreferences
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Build.VERSION.SDK_INT
+import androidx.annotation.RequiresApi
import androidx.annotation.VisibleForTesting
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -16,6 +21,7 @@ import org.mozilla.fenix.Config
import org.mozilla.fenix.GleanMetrics.Events
import org.mozilla.fenix.GleanMetrics.FirstSession
import org.mozilla.fenix.GleanMetrics.Pings
+import org.mozilla.fenix.ext.application
import org.mozilla.fenix.ext.settings
class FirstSessionPing(private val context: Context) {
@@ -70,6 +76,7 @@ class FirstSessionPing(private val context: Context) {
},
)
FirstSession.timestamp.set()
+ FirstSession.installSource.set(installSourcePackage())
}
CoroutineScope(Dispatchers.IO).launch {
@@ -81,6 +88,38 @@ class FirstSessionPing(private val context: Context) {
}
}
+ @SuppressLint("NewApi") // Lint cannot resolve 'sdk' as 'SDK_INT' as it's not referenced directly.
+ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+ internal fun installSourcePackage(sdk: Int = SDK_INT) = with(context.application) {
+ if (sdk >= Build.VERSION_CODES.R) {
+ installSourcePackageForBuildMinR(packageManager, packageName)
+ } else {
+ installSourcePackageForBuildMaxQ(packageManager, packageName)
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.R)
+ private fun installSourcePackageForBuildMinR(
+ packageManager: PackageManager,
+ packageName: String,
+ ) = try {
+ packageManager.getInstallSourceInfo(packageName).installingPackageName
+ } catch (e: PackageManager.NameNotFoundException) {
+ Logger.debug("$packageName is not available to the caller")
+ null
+ }.orEmpty()
+
+ private fun installSourcePackageForBuildMaxQ(
+ packageManager: PackageManager,
+ packageName: String,
+ ) = try {
+ @Suppress("DEPRECATION")
+ packageManager.getInstallerPackageName(packageName)
+ } catch (e: IllegalArgumentException) {
+ Logger.debug("$packageName is not installed")
+ null
+ }.orEmpty()
+
/**
* Check that at least one of the metrics values is set before sending the ping.
* Note: it is normal for many of these values to not be set as campaigns do not always
diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/components/metrics/FirstSessionPingTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/components/metrics/FirstSessionPingTest.kt
index c3e920f2ca..10d3a20aa3 100644
--- a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/components/metrics/FirstSessionPingTest.kt
+++ b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/components/metrics/FirstSessionPingTest.kt
@@ -5,6 +5,9 @@
package org.mozilla.fenix.components.metrics
import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Build
+import android.os.Build.VERSION.SDK_INT
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
@@ -12,7 +15,9 @@ import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.spyk
import io.mockk.verify
+import org.junit.Assert.assertEquals
import org.junit.Test
+import org.mozilla.fenix.FenixApplication
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.utils.Settings
@@ -20,10 +25,19 @@ internal class FirstSessionPingTest {
@Test
fun `checkAndSend() triggers the ping if it wasn't marked as triggered`() {
+ val mockedPackageManager: PackageManager = mockk(relaxed = true)
+ mockedPackageManager.configureMockInstallSourcePackage()
+
+ val mockedApplication: FenixApplication = mockk(relaxed = true)
+ every { mockedApplication.packageManager } returns mockedPackageManager
+
val mockedContext: Context = mockk(relaxed = true)
+ every { mockedContext.applicationContext } returns mockedApplication
+
val mockedSettings: Settings = mockk(relaxed = true)
mockkStatic("org.mozilla.fenix.ext.ContextKt")
every { mockedContext.settings() } returns mockedSettings
+
val mockAp = spyk(FirstSessionPing(mockedContext), recordPrivateCalls = true)
every { mockAp.checkMetricsNotEmpty() } returns true
every { mockAp.wasAlreadyTriggered() } returns false
@@ -46,4 +60,116 @@ internal class FirstSessionPingTest {
verify(exactly = 0) { mockAp.triggerPing() }
}
+
+ @Test
+ fun `WHEN build version is R installSourcePackage RETURNS the set package name`() {
+ val mockedPackageManager: PackageManager = mockk(relaxed = true)
+ val testPackageName = "test R"
+ mockedPackageManager.mockInstallSourcePackageForBuildMinR(testPackageName)
+
+ val mockedApplication: FenixApplication = mockk(relaxed = true)
+ every { mockedApplication.packageManager } returns mockedPackageManager
+
+ val mockedContext: Context = mockk(relaxed = true)
+ every { mockedContext.applicationContext } returns mockedApplication
+
+ val result = FirstSessionPing(mockedContext).installSourcePackage(Build.VERSION_CODES.R)
+ assertEquals(testPackageName, result)
+ }
+
+ @Test
+ fun `GIVEN packageManager throws an exception WHEN Build version is R installSourcePackage RETURNS an empty string`() {
+ val mockedPackageManager: PackageManager = mockk(relaxed = true)
+ every { mockedPackageManager.getInstallSourceInfo(any()).installingPackageName } throws PackageManager.NameNotFoundException()
+
+ val mockedApplication: FenixApplication = mockk(relaxed = true)
+ every { mockedApplication.packageManager } returns mockedPackageManager
+
+ val mockedContext: Context = mockk(relaxed = true)
+ every { mockedContext.applicationContext } returns mockedApplication
+
+ val result = FirstSessionPing(mockedContext).installSourcePackage(Build.VERSION_CODES.R)
+ assertEquals("", result)
+ }
+
+ @Test
+ fun `WHEN build version is more than R installSourcePackage RETURNS the set package name`() {
+ val mockedPackageManager: PackageManager = mockk(relaxed = true)
+ val testPackageName = "test > R"
+ mockedPackageManager.mockInstallSourcePackageForBuildMinR(testPackageName)
+
+ val mockedApplication: FenixApplication = mockk(relaxed = true)
+ every { mockedApplication.packageManager } returns mockedPackageManager
+
+ val mockedContext: Context = mockk(relaxed = true)
+ every { mockedContext.applicationContext } returns mockedApplication
+
+ val result =
+ FirstSessionPing(mockedContext).installSourcePackage(Build.VERSION_CODES.R.plus(1))
+ assertEquals(testPackageName, result)
+ }
+
+ @Test
+ fun `GIVEN packageManager throws an exception WHEN Build version is more than R installSourcePackage RETURNS an empty string`() {
+ val mockedPackageManager: PackageManager = mockk(relaxed = true)
+ every { mockedPackageManager.getInstallSourceInfo(any()).installingPackageName } throws PackageManager.NameNotFoundException()
+
+ val mockedApplication: FenixApplication = mockk(relaxed = true)
+ every { mockedApplication.packageManager } returns mockedPackageManager
+
+ val mockedContext: Context = mockk(relaxed = true)
+ every { mockedContext.applicationContext } returns mockedApplication
+
+ val result =
+ FirstSessionPing(mockedContext).installSourcePackage(Build.VERSION_CODES.R.plus(1))
+ assertEquals("", result)
+ }
+
+ @Test
+ fun `WHEN build version is less than R installSourcePackage RETURNS the set package name`() {
+ val mockedPackageManager: PackageManager = mockk(relaxed = true)
+ val testPackageName = "test < R"
+ mockedPackageManager.mockInstallSourcePackageForBuildMaxQ(testPackageName)
+
+ val mockedApplication: FenixApplication = mockk(relaxed = true)
+ every { mockedApplication.packageManager } returns mockedPackageManager
+
+ val mockedContext: Context = mockk(relaxed = true)
+ every { mockedContext.applicationContext } returns mockedApplication
+
+ val result =
+ FirstSessionPing(mockedContext).installSourcePackage(Build.VERSION_CODES.R.minus(1))
+ assertEquals(testPackageName, result)
+ }
+
+ @Test
+ fun `GIVEN packageManager throws an exception WHEN Build version is less than R installSourcePackage RETURNS an empty string`() {
+ val mockedPackageManager: PackageManager = mockk(relaxed = true)
+ @Suppress("DEPRECATION")
+ every { mockedPackageManager.getInstallerPackageName(any()) } throws IllegalArgumentException()
+
+ val mockedApplication: FenixApplication = mockk(relaxed = true)
+ every { mockedApplication.packageManager } returns mockedPackageManager
+
+ val mockedContext: Context = mockk(relaxed = true)
+ every { mockedContext.applicationContext } returns mockedApplication
+
+ val result =
+ FirstSessionPing(mockedContext).installSourcePackage(Build.VERSION_CODES.R.minus(1))
+ assertEquals("", result)
+ }
}
+
+private fun PackageManager.configureMockInstallSourcePackage() =
+ if (SDK_INT >= Build.VERSION_CODES.R) {
+ mockInstallSourcePackageForBuildMinR()
+ } else {
+ mockInstallSourcePackageForBuildMaxQ()
+ }
+
+private fun PackageManager.mockInstallSourcePackageForBuildMinR(packageName: String = "") =
+ every { getInstallSourceInfo(any()).installingPackageName } returns packageName
+
+@Suppress("DEPRECATION")
+private fun PackageManager.mockInstallSourcePackageForBuildMaxQ(packageName: String = "") =
+ every { getInstallerPackageName(any()) } returns packageName
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/LollipopAsyncCodec.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/LollipopAsyncCodec.java
index aaf8810bbb..444ee633b6 100644
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/LollipopAsyncCodec.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/media/LollipopAsyncCodec.java
@@ -8,6 +8,7 @@ import android.media.MediaCodec;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCrypto;
import android.media.MediaFormat;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -134,7 +135,18 @@ import org.mozilla.gecko.util.HardwareCodecCapabilityUtils;
}
/* package */ LollipopAsyncCodec(final String name) throws IOException {
- mCodec = MediaCodec.createByCodecName(name);
+ // Create the codec.
+ // We wrap the call to MediaCodec.createByCodecName in a pair of
+ // clearCallingIdentity / restoreCallingIdentity, so that the resource
+ // gets attributed to this process and not to whichever process was calling us.
+ // This works around a battery usage attribution bug in Android 14+,
+ // see bug 1902077.
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mCodec = MediaCodec.createByCodecName(name);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
@Override
diff --git a/mobile/android/version.txt b/mobile/android/version.txt
index 29810a755b..6f1605c380 100644
--- a/mobile/android/version.txt
+++ b/mobile/android/version.txt
@@ -1 +1 @@
-127.0.1
+127.0.2