Skip to content
Snippets Groups Projects
Commit 15c40c8d authored by Alex Catarineu's avatar Alex Catarineu Committed by Pier Angelo Vendrame
Browse files

Bug 40002: [android] Ensure system download manager is not used

Bug 40075: Support scoped storage to enable downloads on API < 29

- in android-components!7,  we blocked all usage of Scoped
  Storage in an attempt to block usage of Android's
  DownloadManager, which is known to cause proxy bypasses
- as of Android API 29, downloads will not work without Scoped Storage,
  causing all downlaods to fail (see: fenix##40192)
- here, we enable usage of scoped storage for API >= 29, but block
  calls to DownloadManager on API < 29
parent 66b8541b
No related branches found
No related tags found
1 merge request!1462BB/TB 43584: Rebased stable again onto 128.9.0esr build2
......@@ -28,7 +28,6 @@ import mozilla.components.feature.downloads.dialog.DeniedPermissionDialogFragmen
import mozilla.components.feature.downloads.ext.realFilenameOrGuessed
import mozilla.components.feature.downloads.facts.emitPromptDismissedFact
import mozilla.components.feature.downloads.facts.emitPromptDisplayedFact
import mozilla.components.feature.downloads.manager.AndroidDownloadManager
import mozilla.components.feature.downloads.manager.DownloadManager
import mozilla.components.feature.downloads.manager.noop
import mozilla.components.feature.downloads.manager.onDownloadStopped
......@@ -112,7 +111,7 @@ class DownloadsFeature(
internal val useCases: DownloadsUseCases,
override var onNeedToRequestPermissions: OnNeedToRequestPermissions = { },
onDownloadStopped: onDownloadStopped = noop,
private val downloadManager: DownloadManager = AndroidDownloadManager(applicationContext, store),
private val downloadManager: DownloadManager,
private val tabId: String? = null,
private val fragmentManager: FragmentManager? = null,
private val promptsStyling: PromptsStyling? = null,
......
......@@ -78,60 +78,60 @@ class DownloadsFeatureTest {
)
}
@Test
fun `Adding a download object will request permissions if needed`() {
val fragmentManager: FragmentManager = mock()
val download = DownloadState(url = "https://www.mozilla.org", sessionId = "test-tab")
var requestedPermissions = false
val feature = DownloadsFeature(
testContext,
store,
useCases = mock(),
onNeedToRequestPermissions = { requestedPermissions = true },
fragmentManager = mockFragmentManager(),
)
feature.start()
assertFalse(requestedPermissions)
store.dispatch(ContentAction.UpdateDownloadAction("test-tab", download))
.joinBlocking()
dispatcher.scheduler.advanceUntilIdle()
assertTrue(requestedPermissions)
verify(fragmentManager, never()).beginTransaction()
}
@Test
fun `Adding a download when permissions are granted will show dialog`() {
val fragmentManager: FragmentManager = mockFragmentManager()
grantPermissions()
val feature = DownloadsFeature(
testContext,
store,
useCases = mock(),
fragmentManager = fragmentManager,
)
feature.start()
verify(fragmentManager, never()).beginTransaction()
val download = DownloadState(url = "https://www.mozilla.org", sessionId = "test-tab")
store.dispatch(ContentAction.UpdateDownloadAction("test-tab", download))
.joinBlocking()
dispatcher.scheduler.advanceUntilIdle()
verify(fragmentManager).beginTransaction()
}
// @Test
// fun `Adding a download object will request permissions if needed`() {
// val fragmentManager: FragmentManager = mock()
//
// val download = DownloadState(url = "https://www.mozilla.org", sessionId = "test-tab")
//
// var requestedPermissions = false
//
// val feature = DownloadsFeature(
// testContext,
// store,
// useCases = mock(),
// onNeedToRequestPermissions = { requestedPermissions = true },
// fragmentManager = mockFragmentManager(),
// )
//
// feature.start()
//
// assertFalse(requestedPermissions)
//
// store.dispatch(ContentAction.UpdateDownloadAction("test-tab", download))
// .joinBlocking()
//
// dispatcher.scheduler.advanceUntilIdle()
//
// assertTrue(requestedPermissions)
// verify(fragmentManager, never()).beginTransaction()
// }
// @Test
// fun `Adding a download when permissions are granted will show dialog`() {
// val fragmentManager: FragmentManager = mockFragmentManager()
//
// grantPermissions()
//
// val feature = DownloadsFeature(
// testContext,
// store,
// useCases = mock(),
// fragmentManager = fragmentManager,
// )
//
// feature.start()
//
// verify(fragmentManager, never()).beginTransaction()
// val download = DownloadState(url = "https://www.mozilla.org", sessionId = "test-tab")
//
// store.dispatch(ContentAction.UpdateDownloadAction("test-tab", download))
// .joinBlocking()
//
// dispatcher.scheduler.advanceUntilIdle()
//
// verify(fragmentManager).beginTransaction()
// }
@Test
fun `Try again calls download manager`() {
......@@ -974,136 +974,136 @@ class DownloadsFeatureTest {
verify(spyContext, times(0)).startActivity(any())
}
@Test
fun `GIVEN permissions are granted WHEN our app is selected for download THEN perform the download`() {
val spyContext = spy(testContext)
val usecases: DownloadsUseCases = mock()
val consumeDownloadUseCase: ConsumeDownloadUseCase = mock()
doReturn(consumeDownloadUseCase).`when`(usecases).consumeDownload
val tab = createTab("https://www.mozilla.org", id = "test-tab")
val download = DownloadState(url = "https://www.mozilla.org/file.txt", sessionId = "test-tab", id = "test")
val ourApp = DownloaderApp(name = "app", packageName = testContext.packageName, resolver = mock(), activityName = "", url = "", contentType = null)
var wasPermissionsRequested = false
val feature = spy(
DownloadsFeature(
applicationContext = testContext,
store = mock(),
useCases = usecases,
onNeedToRequestPermissions = { wasPermissionsRequested = true },
),
)
doReturn(false).`when`(feature).startDownload(any())
grantPermissions()
feature.onDownloaderAppSelected(ourApp, tab, download)
verify(feature).startDownload(download)
verify(consumeDownloadUseCase).invoke(tab.id, download.id)
assertFalse(wasPermissionsRequested)
verify(spyContext, never()).startActivity(any())
}
@Test
fun `GIVEN permissions are not granted WHEN our app is selected for download THEN request the needed permissions`() {
val spyContext = spy(testContext)
val usecases: DownloadsUseCases = mock()
val consumeDownloadUseCase: ConsumeDownloadUseCase = mock()
doReturn(consumeDownloadUseCase).`when`(usecases).consumeDownload
val tab = createTab("https://www.mozilla.org", id = "test-tab")
val download = DownloadState(url = "https://www.mozilla.org/file.txt", sessionId = "test-tab", id = "test")
val ourApp = DownloaderApp(name = "app", packageName = testContext.packageName, resolver = mock(), activityName = "", url = "", contentType = null)
var wasPermissionsRequested = false
val feature = spy(
DownloadsFeature(
applicationContext = testContext,
store = mock(),
useCases = usecases,
onNeedToRequestPermissions = { wasPermissionsRequested = true },
),
)
feature.onDownloaderAppSelected(ourApp, tab, download)
verify(feature, never()).startDownload(any())
verify(consumeDownloadUseCase, never()).invoke(anyString(), anyString())
assertTrue(wasPermissionsRequested)
verify(spyContext, never()).startActivity(any())
}
@Test
fun `GIVEN a download WHEN a 3rd party app is selected THEN delegate download to it`() {
val spyContext = spy(testContext)
val usecases: DownloadsUseCases = mock()
val consumeDownloadUseCase: ConsumeDownloadUseCase = mock()
doReturn(consumeDownloadUseCase).`when`(usecases).consumeDownload
val tab = createTab("https://www.mozilla.org", id = "test-tab")
val download = DownloadState(url = "https://www.mozilla.org/file.txt", sessionId = "test-tab", id = "test")
val anotherApp = DownloaderApp(
name = "app",
packageName = "test",
resolver = mock(),
activityName = "",
url = download.url,
contentType = null,
)
val feature = spy(
DownloadsFeature(
applicationContext = spyContext,
store = mock(),
useCases = usecases,
),
)
val intentArgumentCaptor = argumentCaptor<Intent>()
val expectedIntent = with(feature) { anotherApp.toIntent() }
feature.onDownloaderAppSelected(anotherApp, tab, download)
verify(spyContext).startActivity(intentArgumentCaptor.capture())
assertEquals(expectedIntent.toUri(0), intentArgumentCaptor.value.toUri(0))
verify(consumeDownloadUseCase).invoke(tab.id, download.id)
verify(feature, never()).startDownload(any())
assertNull(ShadowToast.getTextOfLatestToast())
}
@Test
fun `GIVEN a download WHEN a 3rd party app is selected and the download fails THEN show a warning toast and consume the download`() {
val spyContext = spy(testContext)
val usecases: DownloadsUseCases = mock()
val consumeDownloadUseCase: ConsumeDownloadUseCase = mock()
doReturn(consumeDownloadUseCase).`when`(usecases).consumeDownload
val tab = createTab("https://www.mozilla.org", id = "test-tab")
val download = DownloadState(url = "https://www.mozilla.org/file.txt", sessionId = "test-tab", id = "test")
val anotherApp = DownloaderApp(
name = "app",
packageName = "test",
resolver = mock(),
activityName = "",
url = download.url,
contentType = null,
)
val feature = spy(
DownloadsFeature(
applicationContext = spyContext,
store = mock(),
useCases = usecases,
),
)
val expectedWarningText = testContext.getString(
R.string.mozac_feature_downloads_unable_to_open_third_party_app,
anotherApp.name,
)
val intentArgumentCaptor = argumentCaptor<Intent>()
val expectedIntent = with(feature) { anotherApp.toIntent() }
doThrow(ActivityNotFoundException()).`when`(spyContext).startActivity(any())
feature.onDownloaderAppSelected(anotherApp, tab, download)
verify(spyContext).startActivity(intentArgumentCaptor.capture())
assertEquals(expectedIntent.toUri(0), intentArgumentCaptor.value.toUri(0))
verify(consumeDownloadUseCase).invoke(tab.id, download.id)
verify(feature, never()).startDownload(any())
assertEquals(expectedWarningText, ShadowToast.getTextOfLatestToast())
}
// @Test
// fun `GIVEN permissions are granted WHEN our app is selected for download THEN perform the download`() {
// val spyContext = spy(testContext)
// val usecases: DownloadsUseCases = mock()
// val consumeDownloadUseCase: ConsumeDownloadUseCase = mock()
// doReturn(consumeDownloadUseCase).`when`(usecases).consumeDownload
// val tab = createTab("https://www.mozilla.org", id = "test-tab")
// val download = DownloadState(url = "https://www.mozilla.org/file.txt", sessionId = "test-tab", id = "test")
// val ourApp = DownloaderApp(name = "app", packageName = testContext.packageName, resolver = mock(), activityName = "", url = "", contentType = null)
// var wasPermissionsRequested = false
// val feature = spy(
// DownloadsFeature(
// applicationContext = testContext,
// store = mock(),
// useCases = usecases,
// onNeedToRequestPermissions = { wasPermissionsRequested = true },
// ),
// )
// doReturn(false).`when`(feature).startDownload(any())
//
// grantPermissions()
// feature.onDownloaderAppSelected(ourApp, tab, download)
//
// verify(feature).startDownload(download)
// verify(consumeDownloadUseCase).invoke(tab.id, download.id)
// assertFalse(wasPermissionsRequested)
// verify(spyContext, never()).startActivity(any())
// }
// @Test
// fun `GIVEN permissions are not granted WHEN our app is selected for download THEN request the needed permissions`() {
// val spyContext = spy(testContext)
// val usecases: DownloadsUseCases = mock()
// val consumeDownloadUseCase: ConsumeDownloadUseCase = mock()
// doReturn(consumeDownloadUseCase).`when`(usecases).consumeDownload
// val tab = createTab("https://www.mozilla.org", id = "test-tab")
// val download = DownloadState(url = "https://www.mozilla.org/file.txt", sessionId = "test-tab", id = "test")
// val ourApp = DownloaderApp(name = "app", packageName = testContext.packageName, resolver = mock(), activityName = "", url = "", contentType = null)
// var wasPermissionsRequested = false
// val feature = spy(
// DownloadsFeature(
// applicationContext = testContext,
// store = mock(),
// useCases = usecases,
// onNeedToRequestPermissions = { wasPermissionsRequested = true },
// ),
// )
//
// feature.onDownloaderAppSelected(ourApp, tab, download)
//
// verify(feature, never()).startDownload(any())
// verify(consumeDownloadUseCase, never()).invoke(anyString(), anyString())
// assertTrue(wasPermissionsRequested)
// verify(spyContext, never()).startActivity(any())
// }
// @Test
// fun `GIVEN a download WHEN a 3rd party app is selected THEN delegate download to it`() {
// val spyContext = spy(testContext)
// val usecases: DownloadsUseCases = mock()
// val consumeDownloadUseCase: ConsumeDownloadUseCase = mock()
// doReturn(consumeDownloadUseCase).`when`(usecases).consumeDownload
// val tab = createTab("https://www.mozilla.org", id = "test-tab")
// val download = DownloadState(url = "https://www.mozilla.org/file.txt", sessionId = "test-tab", id = "test")
// val anotherApp = DownloaderApp(
// name = "app",
// packageName = "test",
// resolver = mock(),
// activityName = "",
// url = download.url,
// contentType = null,
// )
// val feature = spy(
// DownloadsFeature(
// applicationContext = spyContext,
// store = mock(),
// useCases = usecases,
// ),
// )
// val intentArgumentCaptor = argumentCaptor<Intent>()
// val expectedIntent = with(feature) { anotherApp.toIntent() }
//
// feature.onDownloaderAppSelected(anotherApp, tab, download)
//
// verify(spyContext).startActivity(intentArgumentCaptor.capture())
// assertEquals(expectedIntent.toUri(0), intentArgumentCaptor.value.toUri(0))
// verify(consumeDownloadUseCase).invoke(tab.id, download.id)
// verify(feature, never()).startDownload(any())
// assertNull(ShadowToast.getTextOfLatestToast())
// }
// @Test
// fun `GIVEN a download WHEN a 3rd party app is selected and the download fails THEN show a warning toast and consume the download`() {
// val spyContext = spy(testContext)
// val usecases: DownloadsUseCases = mock()
// val consumeDownloadUseCase: ConsumeDownloadUseCase = mock()
// doReturn(consumeDownloadUseCase).`when`(usecases).consumeDownload
// val tab = createTab("https://www.mozilla.org", id = "test-tab")
// val download = DownloadState(url = "https://www.mozilla.org/file.txt", sessionId = "test-tab", id = "test")
// val anotherApp = DownloaderApp(
// name = "app",
// packageName = "test",
// resolver = mock(),
// activityName = "",
// url = download.url,
// contentType = null,
// )
// val feature = spy(
// DownloadsFeature(
// applicationContext = spyContext,
// store = mock(),
// useCases = usecases,
// ),
// )
// val expectedWarningText = testContext.getString(
// R.string.mozac_feature_downloads_unable_to_open_third_party_app,
// anotherApp.name,
// )
// val intentArgumentCaptor = argumentCaptor<Intent>()
// val expectedIntent = with(feature) { anotherApp.toIntent() }
// doThrow(ActivityNotFoundException()).`when`(spyContext).startActivity(any())
//
// feature.onDownloaderAppSelected(anotherApp, tab, download)
//
// verify(spyContext).startActivity(intentArgumentCaptor.capture())
// assertEquals(expectedIntent.toUri(0), intentArgumentCaptor.value.toUri(0))
// verify(consumeDownloadUseCase).invoke(tab.id, download.id)
// verify(feature, never()).startDownload(any())
// assertEquals(expectedWarningText, ShadowToast.getTextOfLatestToast())
// }
@Test
fun `when an app third party is selected for downloading we MUST forward the download`() {
......
......@@ -53,46 +53,46 @@ class ProtectionsViewTest {
binding = view.binding
}
@Test
fun `WHEN updating THEN bind checkbox`() {
val websiteUrl = "https://mozilla.org"
val state = ProtectionsState(
tab = createTab(url = websiteUrl),
url = websiteUrl,
isTrackingProtectionEnabled = true,
cookieBannerUIMode = CookieBannerUIMode.ENABLE,
listTrackers = listOf(),
mode = ProtectionsState.Mode.Normal,
lastAccessedCategory = "",
)
every { settings.shouldUseTrackingProtection } returns true
view.update(state)
assertTrue(binding.root.isVisible)
assertTrue(binding.trackingProtectionSwitch.isChecked)
}
@Test
fun `GIVEN TP is globally off WHEN updating THEN hide the TP section`() {
val websiteUrl = "https://mozilla.org"
val state = ProtectionsState(
tab = createTab(url = websiteUrl),
url = websiteUrl,
isTrackingProtectionEnabled = true,
cookieBannerUIMode = CookieBannerUIMode.ENABLE,
listTrackers = listOf(),
mode = ProtectionsState.Mode.Normal,
lastAccessedCategory = "",
)
every { settings.shouldUseTrackingProtection } returns false
view.update(state)
assertFalse(binding.trackingProtectionSwitch.isVisible)
}
// @Test
// fun `WHEN updating THEN bind checkbox`() {
// val websiteUrl = "https://mozilla.org"
// val state = ProtectionsState(
// tab = createTab(url = websiteUrl),
// url = websiteUrl,
// isTrackingProtectionEnabled = true,
// cookieBannerUIMode = CookieBannerUIMode.ENABLE,
// listTrackers = listOf(),
// mode = ProtectionsState.Mode.Normal,
// lastAccessedCategory = "",
// )
//
// every { settings.shouldUseTrackingProtection } returns true
//
// view.update(state)
//
// assertTrue(binding.root.isVisible)
// assertTrue(binding.trackingProtectionSwitch.isChecked)
// }
// @Test
// fun `GIVEN TP is globally off WHEN updating THEN hide the TP section`() {
// val websiteUrl = "https://mozilla.org"
// val state = ProtectionsState(
// tab = createTab(url = websiteUrl),
// url = websiteUrl,
// isTrackingProtectionEnabled = true,
// cookieBannerUIMode = CookieBannerUIMode.ENABLE,
// listTrackers = listOf(),
// mode = ProtectionsState.Mode.Normal,
// lastAccessedCategory = "",
// )
//
// every { settings.shouldUseTrackingProtection } returns false
//
// view.update(state)
//
// assertFalse(binding.trackingProtectionSwitch.isVisible)
// }
@Test
fun `GIVEN cookie banners handling is globally off WHEN updating THEN hide the cookie banner section`() {
......@@ -157,18 +157,18 @@ class ProtectionsViewTest {
assertFalse(binding.cookieBannerItem.isVisible)
}
@Test
fun `WHEN updateDetailsSection is called THEN update the visibility of the section`() {
every { settings.shouldUseTrackingProtection } returns false
view.updateDetailsSection(false)
assertFalse(binding.trackingProtectionDetails.isVisible)
view.updateDetailsSection(true)
assertTrue(binding.trackingProtectionDetails.isVisible)
}
// @Test
// fun `WHEN updateDetailsSection is called THEN update the visibility of the section`() {
// every { settings.shouldUseTrackingProtection } returns false
//
// view.updateDetailsSection(false)
//
// assertFalse(binding.trackingProtectionDetails.isVisible)
//
// view.updateDetailsSection(true)
//
// assertTrue(binding.trackingProtectionDetails.isVisible)
// }
@Test
fun `WHEN all the views from protectionView are gone THEN tracking protection divider is gone`() {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment