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

Commit f37e5423 authored by Fahim M. Choudhury's avatar Fahim M. Choudhury
Browse files

feat: block installation of third-party store apps when parental control is active

Similar to 'not-working' and 'zero-privacy' apps, a list of third-party app stores, stored in the [JSON](https://gitlab.e.foundation/e/os/blocklist-app-lounge/-/blob/main/app-lounge-warning-list.json#L91) at GitLab repo, are downloaded and saved into filesystem cache.

When trying to install such an app, App Lounge checks if the package is already in the blocklist.

If any such package is already installed on the device prior to activating parental control, the package is checked against the blocklist and the data is sent to the Parental Control app via the AgeRatingProvider. Thus, parental control blocks the app from running.
parent 55a166e3
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
/*
 * Apps  Quickly and easily install Android apps onto your device!
 * Copyright (C) 2022  E FOUNDATION
 * Copyright (C) 2022-2024 E FOUNDATION
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
@@ -14,6 +13,7 @@
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */

package foundation.e.apps.data.blockedApps
@@ -22,5 +22,6 @@ import com.google.gson.annotations.SerializedName

data class AppWarningInfo(
    @SerializedName("not_working_apps") val notWorkingApps: List<String>,
    @SerializedName("zero_privacy_apps") val zeroPrivacyApps: List<String>
    @SerializedName("zero_privacy_apps") val zeroPrivacyApps: List<String>,
    @SerializedName("third_party_store_apps") val thirdPartyStoreApps: List<String> = emptyList()
)
+7 −3
Original line number Diff line number Diff line
/*
 * Apps  Quickly and easily install Android apps onto your device!
 * Copyright (C) 2022  E FOUNDATION
 * Copyright (C) 2022-2024 E FOUNDATION
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
@@ -14,6 +13,7 @@
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 */
package foundation.e.apps.data.blockedApps

@@ -53,6 +53,10 @@ class BlockedAppRepository @Inject constructor(
    fun isPrivacyScoreZero(packageName: String) =
        blockedAppInfoList?.zeroPrivacyApps?.contains(packageName) ?: false

    fun isThirdPartyStoreApp(packageName: String): Boolean {
        return blockedAppInfoList?.thirdPartyStoreApps?.contains(packageName) ?: false
    }

    suspend fun fetchUpdateOfAppWarningList(): Boolean =
        suspendCancellableCoroutine { continuation ->
            downloadManager.downloadFileInCache(
@@ -78,7 +82,7 @@ class BlockedAppRepository @Inject constructor(
            gson.fromJson(blockedAppInfoJson, AppWarningInfo::class.java)
        } catch (exception: Exception) {
            Timber.e(exception.localizedMessage ?: "", exception)
            AppWarningInfo(listOf(), listOf())
            AppWarningInfo(listOf(), listOf(), listOf())
        }
    }
}
+12 −3
Original line number Diff line number Diff line
@@ -21,13 +21,14 @@ package foundation.e.apps.domain
import com.aurora.gplayapi.data.models.ContentRating
import foundation.e.apps.data.ResultSupreme
import foundation.e.apps.data.application.apps.AppsApi
import foundation.e.apps.data.parentalcontrol.Age
import foundation.e.apps.data.parentalcontrol.ParentalControlRepository
import foundation.e.apps.data.blockedApps.BlockedAppRepository
import foundation.e.apps.data.enums.Origin
import foundation.e.apps.data.enums.Type
import foundation.e.apps.data.install.models.AppInstall
import foundation.e.apps.data.parentalcontrol.fdroid.FDroidAntiFeatureRepository
import foundation.e.apps.data.parentalcontrol.Age
import foundation.e.apps.data.parentalcontrol.ContentRatingDao
import foundation.e.apps.data.parentalcontrol.ParentalControlRepository
import foundation.e.apps.data.parentalcontrol.fdroid.FDroidAntiFeatureRepository
import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingGroup
import foundation.e.apps.data.parentalcontrol.googleplay.GPlayContentRatingRepository
import foundation.e.apps.domain.model.ContentRatingValidity
@@ -38,6 +39,7 @@ class ValidateAppAgeLimitUseCase @Inject constructor(
    private val gPlayContentRatingRepository: GPlayContentRatingRepository,
    private val fDroidAntiFeatureRepository: FDroidAntiFeatureRepository,
    private val parentalControlRepository: ParentalControlRepository,
    private val blockedAppRepository: BlockedAppRepository,
    private val appsApi: AppsApi,
    private val contentRatingDao: ContentRatingDao,
) {
@@ -60,7 +62,10 @@ class ValidateAppAgeLimitUseCase @Inject constructor(
                data = ContentRatingValidity(true)
            )

            isThirdPartyStoreApp(app) -> ResultSupreme.Success(data = ContentRatingValidity(false))

            isKnownNsfwApp(app) -> ResultSupreme.Success(data = ContentRatingValidity(false))

            isCleanApkApp(app) -> ResultSupreme.Success(
                data = ContentRatingValidity(isValid = !isNsfwAppByCleanApkApi(app))
            )
@@ -74,6 +79,10 @@ class ValidateAppAgeLimitUseCase @Inject constructor(
        }
    }

    private fun isThirdPartyStoreApp(app: AppInstall): Boolean {
        return blockedAppRepository.isThirdPartyStoreApp(app.packageName)
    }

    private fun isGitlabApp(app: AppInstall): Boolean {
        return app.origin == Origin.GITLAB_RELEASES
    }
+11 −2
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import foundation.e.apps.contract.ParentalControlContract.PATH_BLOCKLIST
import foundation.e.apps.contract.ParentalControlContract.PATH_LOGIN_TYPE
import foundation.e.apps.contract.ParentalControlContract.getAppLoungeProviderAuthority
import foundation.e.apps.data.ResultSupreme
import foundation.e.apps.data.blockedApps.BlockedAppRepository
import foundation.e.apps.data.enums.Origin
import foundation.e.apps.data.install.models.AppInstall
import foundation.e.apps.data.login.AuthenticatorRepository
@@ -73,6 +74,7 @@ class AgeRatingProvider : ContentProvider() {
        fun provideDataStoreManager(): DataStoreManager
        fun provideNotificationManager(): NotificationManager
        fun provideContentRatingDao(): ContentRatingDao
        fun provideBlockedAppRepository(): BlockedAppRepository
    }

    companion object {
@@ -88,6 +90,7 @@ class AgeRatingProvider : ContentProvider() {
    private lateinit var dataStoreManager: DataStoreManager
    private lateinit var notificationManager: NotificationManager
    private lateinit var contentRatingDao: ContentRatingDao
    private lateinit var blockedAppRepository: BlockedAppRepository

    private enum class UriCode(val code: Int) {
        LoginType(1),
@@ -228,7 +231,7 @@ class AgeRatingProvider : ContentProvider() {
        )
    }

    private suspend fun isAppValidRegardingNSWF(packageName: String): Boolean {
    private suspend fun isAppValidRegardingNSFW(packageName: String): Boolean {
        val fakeAppInstall = AppInstall(
            packageName = packageName,
            origin = Origin.CLEANAPK,
@@ -241,12 +244,17 @@ class AgeRatingProvider : ContentProvider() {
        return when {
            validateAppAgeLimitUseCase.isParentalControlDisabled() -> true
            !isInitialized() -> false
            !isAppValidRegardingNSWF(packageName) -> false
            isThirdPartyStoreApp(packageName) -> false
            !isAppValidRegardingNSFW(packageName) -> false
            isAppValidRegardingAge(packageName) == false -> false
            else -> true
        }
    }

    private fun isThirdPartyStoreApp(packageName: String): Boolean {
        return blockedAppRepository.isThirdPartyStoreApp(packageName)
    }

    private suspend fun compileAppBlockList(
        cursor: MatrixCursor,
        packageNames: List<String>,
@@ -302,6 +310,7 @@ class AgeRatingProvider : ContentProvider() {
        dataStoreManager = hiltEntryPoint.provideDataStoreManager()
        notificationManager = hiltEntryPoint.provideNotificationManager()
        contentRatingDao = hiltEntryPoint.provideContentRatingDao()
        blockedAppRepository = hiltEntryPoint.provideBlockedAppRepository()

        return true
    }