mirror of
https://github.com/bitwarden/android.git
synced 2025-12-12 00:08:00 -06:00
[BWA-162] Add getPackageInstallationSourceOrNull to BitwardenPackageManager (#5418)
This commit is contained in:
parent
b94d59ba6b
commit
5f525d9d95
@ -4,6 +4,14 @@ package com.bitwarden.data.manager
|
|||||||
* Abstraction for interacting with Android package manager.
|
* Abstraction for interacting with Android package manager.
|
||||||
*/
|
*/
|
||||||
interface BitwardenPackageManager {
|
interface BitwardenPackageManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the package installation source. The result may be `null` if the package is not
|
||||||
|
* installed, the package is a system application, or the installing application has been
|
||||||
|
* uninstalled.
|
||||||
|
*/
|
||||||
|
fun getPackageInstallationSourceOrNull(packageName: String): String?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the package is installed.
|
* Checks if the package is installed.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package com.bitwarden.data.manager
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
import com.bitwarden.core.util.isBuildVersionAtLeast
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Primary implementation of [BitwardenPackageManager].
|
* Primary implementation of [BitwardenPackageManager].
|
||||||
@ -12,9 +13,24 @@ class BitwardenPackageManagerImpl(
|
|||||||
|
|
||||||
private val nativePackageManager = context.packageManager
|
private val nativePackageManager = context.packageManager
|
||||||
|
|
||||||
|
override fun getPackageInstallationSourceOrNull(packageName: String): String? =
|
||||||
|
try {
|
||||||
|
if (isBuildVersionAtLeast(Build.VERSION_CODES.R)) {
|
||||||
|
nativePackageManager
|
||||||
|
.getInstallSourceInfo(packageName)
|
||||||
|
.installingPackageName
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
nativePackageManager
|
||||||
|
.getInstallerPackageName(packageName)
|
||||||
|
}
|
||||||
|
} catch (_: PackageManager.NameNotFoundException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
override fun isPackageInstalled(packageName: String): Boolean {
|
override fun isPackageInstalled(packageName: String): Boolean {
|
||||||
return try {
|
return try {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
if (isBuildVersionAtLeast(Build.VERSION_CODES.TIRAMISU)) {
|
||||||
nativePackageManager.getApplicationInfo(
|
nativePackageManager.getApplicationInfo(
|
||||||
packageName,
|
packageName,
|
||||||
PackageManager.ApplicationInfoFlags.of(0L),
|
PackageManager.ApplicationInfoFlags.of(0L),
|
||||||
|
|||||||
@ -3,13 +3,19 @@ package com.bitwarden.data.manager
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.ApplicationInfo
|
import android.content.pm.ApplicationInfo
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import com.bitwarden.core.util.isBuildVersionAtLeast
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import org.junit.Assert.assertEquals
|
import io.mockk.mockkStatic
|
||||||
import org.junit.Assert.assertFalse
|
import io.mockk.unmockkStatic
|
||||||
import org.junit.Assert.assertNull
|
import io.mockk.verify
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.jupiter.api.AfterEach
|
||||||
import org.junit.Test
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
|
import org.junit.jupiter.api.Assertions.assertFalse
|
||||||
|
import org.junit.jupiter.api.Assertions.assertNull
|
||||||
|
import org.junit.jupiter.api.Assertions.assertTrue
|
||||||
|
import org.junit.jupiter.api.BeforeEach
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
class BitwardenPackageManagerTest {
|
class BitwardenPackageManagerTest {
|
||||||
|
|
||||||
@ -19,10 +25,33 @@ class BitwardenPackageManagerTest {
|
|||||||
}
|
}
|
||||||
private val bitwardenPackageManager = BitwardenPackageManagerImpl(context)
|
private val bitwardenPackageManager = BitwardenPackageManagerImpl(context)
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
fun setUp() {
|
||||||
|
mockkStatic(
|
||||||
|
PackageManager.ApplicationInfoFlags::of,
|
||||||
|
::isBuildVersionAtLeast,
|
||||||
|
)
|
||||||
|
// Set the default API level to simulate the latest version
|
||||||
|
every { isBuildVersionAtLeast(any()) } returns true
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
fun tearDown() {
|
||||||
|
unmockkStatic(
|
||||||
|
PackageManager.ApplicationInfoFlags::of,
|
||||||
|
::isBuildVersionAtLeast,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `isPackageInstalled returns true for installed package`() {
|
fun `isPackageInstalled returns true for installed package`() {
|
||||||
val packageName = "com.example.installed"
|
val packageName = "com.example.installed"
|
||||||
every { mockPackageManager.getApplicationInfo(packageName, 0) } returns ApplicationInfo()
|
every {
|
||||||
|
mockPackageManager.getApplicationInfo(
|
||||||
|
packageName,
|
||||||
|
PackageManager.ApplicationInfoFlags.of(0L),
|
||||||
|
)
|
||||||
|
} returns ApplicationInfo()
|
||||||
val result = bitwardenPackageManager.isPackageInstalled(packageName)
|
val result = bitwardenPackageManager.isPackageInstalled(packageName)
|
||||||
assertTrue(result)
|
assertTrue(result)
|
||||||
}
|
}
|
||||||
@ -31,7 +60,10 @@ class BitwardenPackageManagerTest {
|
|||||||
fun `isPackageInstalled returns false for non existent package`() {
|
fun `isPackageInstalled returns false for non existent package`() {
|
||||||
val packageName = "com.example.nonexistent"
|
val packageName = "com.example.nonexistent"
|
||||||
every {
|
every {
|
||||||
mockPackageManager.getApplicationInfo(packageName, 0)
|
mockPackageManager.getApplicationInfo(
|
||||||
|
packageName,
|
||||||
|
PackageManager.ApplicationInfoFlags.of(0L),
|
||||||
|
)
|
||||||
} throws PackageManager.NameNotFoundException()
|
} throws PackageManager.NameNotFoundException()
|
||||||
val result = bitwardenPackageManager.isPackageInstalled(packageName)
|
val result = bitwardenPackageManager.isPackageInstalled(packageName)
|
||||||
assertFalse(result)
|
assertFalse(result)
|
||||||
@ -41,7 +73,10 @@ class BitwardenPackageManagerTest {
|
|||||||
fun `isPackageInstalled handles empty package name`() {
|
fun `isPackageInstalled handles empty package name`() {
|
||||||
val packageName = ""
|
val packageName = ""
|
||||||
every {
|
every {
|
||||||
mockPackageManager.getApplicationInfo(packageName, 0)
|
mockPackageManager.getApplicationInfo(
|
||||||
|
packageName,
|
||||||
|
PackageManager.ApplicationInfoFlags.of(0L),
|
||||||
|
)
|
||||||
} throws PackageManager.NameNotFoundException()
|
} throws PackageManager.NameNotFoundException()
|
||||||
val result = bitwardenPackageManager.isPackageInstalled(packageName)
|
val result = bitwardenPackageManager.isPackageInstalled(packageName)
|
||||||
assertFalse(result)
|
assertFalse(result)
|
||||||
@ -51,12 +86,27 @@ class BitwardenPackageManagerTest {
|
|||||||
fun `isPackageInstalled handles package name with special characters`() {
|
fun `isPackageInstalled handles package name with special characters`() {
|
||||||
val packageName = "com.example.invalid name!"
|
val packageName = "com.example.invalid name!"
|
||||||
every {
|
every {
|
||||||
mockPackageManager.getApplicationInfo(packageName, 0)
|
mockPackageManager.getApplicationInfo(
|
||||||
|
packageName,
|
||||||
|
PackageManager.ApplicationInfoFlags.of(0L),
|
||||||
|
)
|
||||||
} throws PackageManager.NameNotFoundException()
|
} throws PackageManager.NameNotFoundException()
|
||||||
val result = bitwardenPackageManager.isPackageInstalled(packageName)
|
val result = bitwardenPackageManager.isPackageInstalled(packageName)
|
||||||
assertFalse(result)
|
assertFalse(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("MaxLineLength")
|
||||||
|
@Test
|
||||||
|
fun `isPackageInstalled invokes correct getApplicationInfo overload when API level is below 33`() {
|
||||||
|
val packageName = "com.example.installed"
|
||||||
|
every { isBuildVersionAtLeast(33) } returns false
|
||||||
|
every {
|
||||||
|
mockPackageManager.getApplicationInfo(packageName, 0)
|
||||||
|
} returns mockk()
|
||||||
|
bitwardenPackageManager.isPackageInstalled(packageName)
|
||||||
|
verify { mockPackageManager.getApplicationInfo(packageName, 0) }
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `getAppLabelForPackageOrNull returns correct label for installed package`() {
|
fun `getAppLabelForPackageOrNull returns correct label for installed package`() {
|
||||||
val packageName = "com.example.installed"
|
val packageName = "com.example.installed"
|
||||||
@ -107,4 +157,59 @@ class BitwardenPackageManagerTest {
|
|||||||
val result = bitwardenPackageManager.getAppLabelForPackageOrNull(packageName)
|
val result = bitwardenPackageManager.getAppLabelForPackageOrNull(packageName)
|
||||||
assertNull(result)
|
assertNull(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `getPackageInstallationSourceOrNull returns correct source for installed package`() {
|
||||||
|
val packageName = "com.example.installed"
|
||||||
|
val installationSource = "com.example.source"
|
||||||
|
every { mockPackageManager.getInstallSourceInfo(packageName) } returns mockk {
|
||||||
|
every { installingPackageName } returns installationSource
|
||||||
|
}
|
||||||
|
val result = bitwardenPackageManager.getPackageInstallationSourceOrNull(packageName)
|
||||||
|
assertEquals(installationSource, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `getPackageInstallationSourceOrNull returns null when installation source is null`() {
|
||||||
|
val packageName = "com.example.installed"
|
||||||
|
every {
|
||||||
|
mockPackageManager.getInstallSourceInfo(packageName)
|
||||||
|
} returns mockk {
|
||||||
|
every { installingPackageName } returns null
|
||||||
|
}
|
||||||
|
val result = bitwardenPackageManager.getPackageInstallationSourceOrNull(packageName)
|
||||||
|
assertNull(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `getPackageInstallationSourceOrNull returns null for non existent package`() {
|
||||||
|
val packageName = "com.example.nonexistent"
|
||||||
|
every {
|
||||||
|
mockPackageManager.getInstallSourceInfo(packageName)
|
||||||
|
} throws PackageManager.NameNotFoundException()
|
||||||
|
val result = bitwardenPackageManager.getPackageInstallationSourceOrNull(packageName)
|
||||||
|
assertNull(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
@Test
|
||||||
|
fun `getPackageInstallationSourceOrNull invokes getInstallerPackageName on API 29 or lower`() {
|
||||||
|
val packageName = "com.example.installed"
|
||||||
|
val installationSource = "com.example.source"
|
||||||
|
every { isBuildVersionAtLeast(30) } returns false
|
||||||
|
every { mockPackageManager.getInstallerPackageName(packageName) } returns installationSource
|
||||||
|
bitwardenPackageManager.getPackageInstallationSourceOrNull(packageName)
|
||||||
|
verify { mockPackageManager.getInstallerPackageName(packageName) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `getPackageInstallationSourceOrNull invokes getInstallSourceInfo on API 30 or higher`() {
|
||||||
|
val packageName = "com.example.installed"
|
||||||
|
val installationSource = "com.example.source"
|
||||||
|
every { mockPackageManager.getInstallSourceInfo(packageName) } returns mockk {
|
||||||
|
every { installingPackageName } returns installationSource
|
||||||
|
}
|
||||||
|
bitwardenPackageManager.getPackageInstallationSourceOrNull(packageName)
|
||||||
|
verify { mockPackageManager.getInstallSourceInfo(packageName) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user