Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Verified Commit 0a3eb252 authored by Fahim M. Choudhury's avatar Fahim M. Choudhury
Browse files

refactor(update): expose full install source details

Add a typed install-source accessor so update ownership decisions can use the correct Android fields.

Keep the old installer-name helper temporarily, but document that it is not valid for ownership checks.
parent 4ec03595
Loading
Loading
Loading
Loading
+658 −0

File added.

Preview size limit exceeded, changes collapsed.

+33 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import androidx.core.content.pm.PackageInfoCompat
import dagger.hilt.android.qualifiers.ApplicationContext
import foundation.e.apps.OpenForTesting
import foundation.e.apps.data.installation.model.AppInstall
import foundation.e.apps.data.installation.model.InstallSourceDetails
import foundation.e.apps.data.installation.model.InstallationSource
import foundation.e.apps.data.installation.model.InstallationType
import foundation.e.apps.domain.model.install.Status
@@ -134,6 +135,38 @@ class AppLoungePackageManager @Inject constructor(
        }
    }

    @Suppress("TooGenericExceptionCaught")
    fun getInstallSourceDetails(packageName: String): InstallSourceDetails {
        return try {
            val installSourceInfo = packageManager.getInstallSourceInfo(packageName)
            InstallSourceDetails(
                initiatingPackageName = installSourceInfo.initiatingPackageName,
                installingPackageName = installSourceInfo.installingPackageName,
                updateOwnerPackageName = installSourceInfo.updateOwnerPackageName,
                readSucceeded = true,
            )
        } catch (e: NameNotFoundException) {
            Timber.e("getInstallSourceDetails -> $packageName : ${e.localizedMessage}")
            InstallSourceDetails(
                readSucceeded = false,
                failureReason = e::class.java.simpleName,
            )
        } catch (e: IllegalArgumentException) {
            Timber.e("getInstallSourceDetails -> $packageName : ${e.localizedMessage}")
            InstallSourceDetails(
                readSucceeded = false,
                failureReason = e::class.java.simpleName,
            )
        } catch (e: RuntimeException) {
            Timber.e(e, "getInstallSourceDetails -> $packageName")
            InstallSourceDetails(
                readSucceeded = false,
                failureReason = e::class.java.simpleName,
            )
        }
    }

    // Do not use this helper for update ownership decisions.
    fun getInstallerName(packageName: String): String {
        return try {
            val installerInfo = packageManager.getInstallSourceInfo(packageName)
+79 −0
Original line number Diff line number Diff line
@@ -19,12 +19,15 @@
package foundation.e.apps.data.install.pkg

import android.content.Context
import android.content.pm.InstallSourceInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import androidx.core.content.pm.PackageInfoCompat
import foundation.e.apps.domain.model.install.Status
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNull
import kotlin.test.assertTrue
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
@@ -114,4 +117,80 @@ class AppLoungePackageManagerTest {
            )
        )
    }

    @Test
    fun getInstallSourceDetails_returnsFullDetailsWhenReadSucceeds() {
        val installSourceInfo = mock(InstallSourceInfo::class.java)
        Mockito.`when`(packageManager.getInstallSourceInfo(testPackageName)).thenReturn(installSourceInfo)
        Mockito.`when`(installSourceInfo.initiatingPackageName).thenReturn("foundation.e.apps")
        Mockito.`when`(installSourceInfo.installingPackageName).thenReturn("com.android.vending")
        Mockito.`when`(installSourceInfo.updateOwnerPackageName).thenReturn("foundation.e.apps")

        val result = appLoungePackageManager.getInstallSourceDetails(testPackageName)

        assertTrue(result.readSucceeded)
        assertEquals("foundation.e.apps", result.initiatingPackageName)
        assertEquals("com.android.vending", result.installingPackageName)
        assertEquals("foundation.e.apps", result.updateOwnerPackageName)
        assertNull(result.failureReason)
    }

    @Test
    fun getInstallSourceDetails_returnsDetailsWhenUpdateOwnerIsNull() {
        val installSourceInfo = mock(InstallSourceInfo::class.java)
        Mockito.`when`(packageManager.getInstallSourceInfo(testPackageName)).thenReturn(installSourceInfo)
        Mockito.`when`(installSourceInfo.initiatingPackageName).thenReturn("foundation.e.apps")
        Mockito.`when`(installSourceInfo.installingPackageName).thenReturn("com.android.vending")
        Mockito.`when`(installSourceInfo.updateOwnerPackageName).thenReturn(null)

        val result = appLoungePackageManager.getInstallSourceDetails(testPackageName)

        assertTrue(result.readSucceeded)
        assertEquals("foundation.e.apps", result.initiatingPackageName)
        assertEquals("com.android.vending", result.installingPackageName)
        assertNull(result.updateOwnerPackageName)
        assertNull(result.failureReason)
    }

    @Test
    fun getInstallSourceDetails_returnsClosedResultWhenPackageMissing() {
        Mockito.`when`(packageManager.getInstallSourceInfo(testPackageName))
            .thenThrow(PackageManager.NameNotFoundException())

        val result = appLoungePackageManager.getInstallSourceDetails(testPackageName)

        assertFalse(result.readSucceeded)
        assertNull(result.initiatingPackageName)
        assertNull(result.installingPackageName)
        assertNull(result.updateOwnerPackageName)
        assertEquals("NameNotFoundException", result.failureReason)
    }

    @Test
    fun getInstallSourceDetails_returnsClosedResultWhenPackageManagerRejectsInput() {
        Mockito.`when`(packageManager.getInstallSourceInfo(testPackageName))
            .thenThrow(IllegalArgumentException("bad package"))

        val result = appLoungePackageManager.getInstallSourceDetails(testPackageName)

        assertFalse(result.readSucceeded)
        assertNull(result.initiatingPackageName)
        assertNull(result.installingPackageName)
        assertNull(result.updateOwnerPackageName)
        assertEquals("IllegalArgumentException", result.failureReason)
    }

    @Test
    fun getInstallSourceDetails_returnsClosedResultWhenRuntimeReadFails() {
        Mockito.`when`(packageManager.getInstallSourceInfo(testPackageName))
            .thenThrow(IllegalStateException("boom"))

        val result = appLoungePackageManager.getInstallSourceDetails(testPackageName)

        assertFalse(result.readSucceeded)
        assertNull(result.initiatingPackageName)
        assertNull(result.installingPackageName)
        assertNull(result.updateOwnerPackageName)
        assertEquals("IllegalStateException", result.failureReason)
    }
}
+9 −0
Original line number Diff line number Diff line
package foundation.e.apps.data.installation.model

data class InstallSourceDetails(
    val initiatingPackageName: String? = null,
    val installingPackageName: String? = null,
    val updateOwnerPackageName: String? = null,
    val readSucceeded: Boolean,
    val failureReason: String? = null,
)