From c8d88ec3364218802bc48257b7766ad8f19a6e45 Mon Sep 17 00:00:00 2001 From: Guillaume Jacquart Date: Wed, 17 Aug 2022 08:49:03 +0000 Subject: 2-Simplify sources modules tree --- api/.gitignore | 1 + api/build.gradle | 90 ++++ api/consumer-rules.pro | 0 api/proguard-rules.pro | 21 + api/src/main/AndroidManifest.xml | 21 + .../e/privacymodules/DependencyInjector.kt | 32 ++ .../privacymodules/location/IFakeLocationModule.kt | 41 ++ .../permissions/APermissionsPrivacyModule.kt | 158 +++++++ .../permissions/IPermissionsPrivacyModule.kt | 127 ++++++ .../privacymodules/permissions/data/AppOpModes.kt | 43 ++ .../permissions/data/ApplicationDescription.kt | 30 ++ .../permissions/data/PermissionDescription.kt | 26 ++ .../trackers/IBlockTrackersPrivacyModule.kt | 96 ++++ .../e/privacymodules/trackers/IDNSBlocker.kt | 26 ++ .../trackers/ITrackTrackersPrivacyModule.kt | 96 ++++ .../e/privacymodules/trackers/Tracker.kt | 28 ++ app/build.gradle | 98 ++-- app/src/google/res/values/strings.xml | 3 - app/src/main/AndroidManifest.xml | 32 +- .../e/privacycentralapp/DependencyContainer.kt | 5 +- .../domain/usecases/FakeLocationStateUseCase.kt | 33 +- .../features/location/FakeLocationFragment.kt | 23 +- .../features/location/FakeLocationViewModel.kt | 16 +- app/src/standalone/res/values-night/colors.xml | 28 ++ app/src/standalone/res/values/colors.xml | 27 ++ app/src/standalone/res/values/strings.xml | 3 + build.gradle | 10 +- dependencies.gradle | 21 +- fakelocation/.gitignore | 1 + fakelocation/build.gradle | 54 +++ fakelocation/consumer-rules.pro | 0 fakelocation/fakelocationdemo/.gitignore | 1 + fakelocation/fakelocationdemo/build.gradle | 71 +++ fakelocation/fakelocationdemo/proguard-rules.pro | 21 + .../fakelocationdemo/src/main/AndroidManifest.xml | 43 ++ .../fakelocationdemo/MainActivity.kt | 209 +++++++++ .../src/main/res/layout/activity_main.xml | 130 ++++++ .../src/main/res/values/colors.xml | 27 ++ .../src/main/res/values/strings.xml | 20 + .../src/main/res/values/themes.xml | 33 ++ fakelocation/proguard-rules.pro | 21 + fakelocation/src/main/AndroidManifest.xml | 32 ++ .../fakelocation/FakeLocationModule.kt | 117 +++++ .../fakelocation/FakeLocationService.kt | 112 +++++ gradle/wrapper/gradle-wrapper.properties | 6 +- permissionsstandalone/.gitignore | 1 + permissionsstandalone/build.gradle | 60 +++ permissionsstandalone/consumer-rules.pro | 0 permissionsstandalone/proguard-rules.pro | 21 + permissionsstandalone/src/main/AndroidManifest.xml | 23 + .../permissions/PermissionsPrivacyModule.kt | 49 ++ settings.gradle | 7 +- trackers/build.gradle | 45 ++ trackers/src/main/AndroidManifest.xml | 37 ++ .../trackers/DNSBlockerRunnable.java | 173 +++++++ .../privacymodules/trackers/DNSBlockerService.java | 86 ++++ .../privacymodules/trackers/ForegroundStarter.java | 42 ++ .../e/privacymodules/trackers/TrackersLogger.java | 83 ++++ .../trackers/api/BlockTrackersPrivacyModule.java | 125 +++++ .../trackers/api/TrackTrackersPrivacyModule.java | 150 ++++++ .../trackers/data/StatsDatabase.java | 507 +++++++++++++++++++++ .../trackers/data/StatsRepository.java | 95 ++++ .../trackers/data/TrackersRepository.java | 71 +++ .../trackers/data/WhitelistRepository.java | 153 +++++++ 64 files changed, 3671 insertions(+), 90 deletions(-) create mode 100644 api/.gitignore create mode 100644 api/build.gradle create mode 100644 api/consumer-rules.pro create mode 100644 api/proguard-rules.pro create mode 100644 api/src/main/AndroidManifest.xml create mode 100644 api/src/main/java/foundation/e/privacymodules/DependencyInjector.kt create mode 100644 api/src/main/java/foundation/e/privacymodules/location/IFakeLocationModule.kt create mode 100644 api/src/main/java/foundation/e/privacymodules/permissions/APermissionsPrivacyModule.kt create mode 100644 api/src/main/java/foundation/e/privacymodules/permissions/IPermissionsPrivacyModule.kt create mode 100644 api/src/main/java/foundation/e/privacymodules/permissions/data/AppOpModes.kt create mode 100644 api/src/main/java/foundation/e/privacymodules/permissions/data/ApplicationDescription.kt create mode 100644 api/src/main/java/foundation/e/privacymodules/permissions/data/PermissionDescription.kt create mode 100644 api/src/main/java/foundation/e/privacymodules/trackers/IBlockTrackersPrivacyModule.kt create mode 100644 api/src/main/java/foundation/e/privacymodules/trackers/IDNSBlocker.kt create mode 100644 api/src/main/java/foundation/e/privacymodules/trackers/ITrackTrackersPrivacyModule.kt create mode 100644 api/src/main/java/foundation/e/privacymodules/trackers/Tracker.kt delete mode 100644 app/src/google/res/values/strings.xml create mode 100644 app/src/standalone/res/values-night/colors.xml create mode 100644 app/src/standalone/res/values/colors.xml create mode 100644 app/src/standalone/res/values/strings.xml create mode 100644 fakelocation/.gitignore create mode 100644 fakelocation/build.gradle create mode 100644 fakelocation/consumer-rules.pro create mode 100644 fakelocation/fakelocationdemo/.gitignore create mode 100644 fakelocation/fakelocationdemo/build.gradle create mode 100644 fakelocation/fakelocationdemo/proguard-rules.pro create mode 100644 fakelocation/fakelocationdemo/src/main/AndroidManifest.xml create mode 100644 fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt create mode 100644 fakelocation/fakelocationdemo/src/main/res/layout/activity_main.xml create mode 100644 fakelocation/fakelocationdemo/src/main/res/values/colors.xml create mode 100644 fakelocation/fakelocationdemo/src/main/res/values/strings.xml create mode 100644 fakelocation/fakelocationdemo/src/main/res/values/themes.xml create mode 100644 fakelocation/proguard-rules.pro create mode 100644 fakelocation/src/main/AndroidManifest.xml create mode 100644 fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationModule.kt create mode 100644 fakelocation/src/main/java/foundation/e/privacymodules/fakelocation/FakeLocationService.kt create mode 100644 permissionsstandalone/.gitignore create mode 100644 permissionsstandalone/build.gradle create mode 100644 permissionsstandalone/consumer-rules.pro create mode 100644 permissionsstandalone/proguard-rules.pro create mode 100644 permissionsstandalone/src/main/AndroidManifest.xml create mode 100644 permissionsstandalone/src/main/java/foundation/e/privacymodules/permissions/PermissionsPrivacyModule.kt create mode 100644 trackers/build.gradle create mode 100644 trackers/src/main/AndroidManifest.xml create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerRunnable.java create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/DNSBlockerService.java create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/ForegroundStarter.java create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/TrackersLogger.java create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/api/BlockTrackersPrivacyModule.java create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/api/TrackTrackersPrivacyModule.java create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsDatabase.java create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/data/StatsRepository.java create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/data/TrackersRepository.java create mode 100644 trackers/src/main/java/foundation/e/privacymodules/trackers/data/WhitelistRepository.java diff --git a/api/.gitignore b/api/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/api/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/api/build.gradle b/api/build.gradle new file mode 100644 index 0000000..b8ced30 --- /dev/null +++ b/api/build.gradle @@ -0,0 +1,90 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'maven-publish' + +android { + compileSdkVersion buildConfig.compileSdk + + defaultConfig { + minSdkVersion buildConfig.minSdk + targetSdkVersion buildConfig.targetSdk + + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + implementation( + Libs.Kotlin.stdlib, + Libs.AndroidX.coreKtxAPI29, + Libs.Coroutines.core + ) +} + +//url "https://gitlab.e.foundation/api/v4/groups/e/privacy-central/-/packages/maven" + +publishing { + publications { + maven(MavenPublication) { + groupId 'foundation.e' + //You can either define these here or get them from project conf elsewhere + artifactId 'privacymodule.api' + version buildConfig.version.name + artifact "$buildDir/outputs/aar/api-release.aar" + //aar artifact you want to publish + + //generate pom nodes for dependencies + pom.withXml { + def dependenciesNode = asNode().appendNode('dependencies') + configurations.implementation.allDependencies.each { dependency -> + if (dependency.name != 'unspecified') { + def dependencyNode = dependenciesNode.appendNode('dependency') + dependencyNode.appendNode('groupId', dependency.group) + dependencyNode.appendNode('artifactId', dependency.name) + dependencyNode.appendNode('version', dependency.version) + } + } + } + repositories { + def ciJobToken = System.getenv("CI_JOB_TOKEN") + def ciApiV4Url = System.getenv("CI_API_V4_URL") + if (ciJobToken != null) { + maven { + url "${ciApiV4Url}/projects/900/packages/maven" + credentials(HttpHeaderCredentials) { + name = 'Job-Token' + value = ciJobToken + } + authentication { + header(HttpHeaderAuthentication) + } + } + } else { + maven { + url "https://gitlab.e.foundation/api/v4/projects/900/packages/maven" + credentials(HttpHeaderCredentials) { + name = "Private-Token" + value = gitLabPrivateToken + // the variable resides in ~/.gradle/gradle.properties + } + authentication { + header(HttpHeaderAuthentication) + } + } + } + } + } + } +} diff --git a/api/consumer-rules.pro b/api/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/api/proguard-rules.pro b/api/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/api/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/api/src/main/AndroidManifest.xml b/api/src/main/AndroidManifest.xml new file mode 100644 index 0000000..937e285 --- /dev/null +++ b/api/src/main/AndroidManifest.xml @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/api/src/main/java/foundation/e/privacymodules/DependencyInjector.kt b/api/src/main/java/foundation/e/privacymodules/DependencyInjector.kt new file mode 100644 index 0000000..bcf82d2 --- /dev/null +++ b/api/src/main/java/foundation/e/privacymodules/DependencyInjector.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2022 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.privacymodules + +import foundation.e.privacymodules.trackers.IDNSBlocker + +object DependencyInjector { + fun initialize( + dnsBlocker: IDNSBlocker + ) { + this.dnsBlocker = dnsBlocker + } + + + lateinit var dnsBlocker: IDNSBlocker + private set +} \ No newline at end of file diff --git a/api/src/main/java/foundation/e/privacymodules/location/IFakeLocationModule.kt b/api/src/main/java/foundation/e/privacymodules/location/IFakeLocationModule.kt new file mode 100644 index 0000000..ecad2a4 --- /dev/null +++ b/api/src/main/java/foundation/e/privacymodules/location/IFakeLocationModule.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.privacymodules.location + +/** + * Manage a fake location on the device. + */ +interface IFakeLocationModule { + /** + * Start to fake the location module. Call [setFakeLocation] after to set the fake + * position. + */ + fun startFakeLocation() + + /** + * Set or update the faked position. + * @param latitude the latitude of the fake position in degrees. + * @param longitude the longitude of the fake position in degrees. + */ + fun setFakeLocation(latitude: Double, longitude: Double) + + /** + * Stop the fake location module, giving back hand to the true location modules. + */ + fun stopFakeLocation() +} diff --git a/api/src/main/java/foundation/e/privacymodules/permissions/APermissionsPrivacyModule.kt b/api/src/main/java/foundation/e/privacymodules/permissions/APermissionsPrivacyModule.kt new file mode 100644 index 0000000..68f7ee1 --- /dev/null +++ b/api/src/main/java/foundation/e/privacymodules/permissions/APermissionsPrivacyModule.kt @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2022 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package foundation.e.privacymodules.permissions + +import android.app.AppOpsManager +import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import android.content.pm.PermissionInfo +import android.content.pm.PermissionInfo.PROTECTION_DANGEROUS +import android.graphics.drawable.Drawable +import android.os.Build +import android.util.Log +import foundation.e.privacymodules.permissions.data.AppOpModes +import foundation.e.privacymodules.permissions.data.ApplicationDescription +import foundation.e.privacymodules.permissions.data.PermissionDescription + +/** + * Implementation of the commons functionality between privileged and standard + * versions of the module. + * @param context an Android context, to retrieve packageManager for example. + */ +abstract class APermissionsPrivacyModule(protected val context: Context): IPermissionsPrivacyModule { + + companion object { + private const val TAG = "PermissionsModule" + } + /** + * @see IPermissionsPrivacyModule.getAllApplications + */ + override fun getAllApplications(): List { + val appInfos = context.packageManager.getInstalledApplications(0) + return appInfos.map { buildApplicationDescription(it, false) } + } + + /** + * @see IPermissionsPrivacyModule.getInstalledApplications + */ + override fun getInstalledApplications(): List { + return context.packageManager.getInstalledApplications(0) + .filter { it.flags and ApplicationInfo.FLAG_SYSTEM == 0 } + .map { buildApplicationDescription(it, false) } + } + + /** + * @see IPermissionsPrivacyModule.getInstalledApplications + */ + override fun getApplicationDescription(packageName: String): ApplicationDescription { + val appDesc = buildApplicationDescription(context.packageManager.getApplicationInfo(packageName, 0), false) + appDesc.icon = getApplicationIcon(appDesc.packageName) + return appDesc + } + + /** + * * @see IPermissionsPrivacyModule.getPermissions + */ + override fun getPermissions(packageName: String): List { + val packageInfo = context.packageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS) + return packageInfo.requestedPermissions?.asList() ?: emptyList() + } + + override fun getPermissionDescription(permissionName: String): PermissionDescription { + val info = context.packageManager.getPermissionInfo(permissionName, 0) + return PermissionDescription( + name = permissionName, + isDangerous = isPermissionsDangerous(info), + group = null, + label = info.loadLabel(context.packageManager), + description = info.loadDescription(context.packageManager) + ) + } + + /** + * @see IPermissionsPrivacyModule.isDangerousPermissionGranted + */ + override fun isDangerousPermissionGranted(packageName: String, permissionName: String): Boolean { + return context.packageManager + .checkPermission(permissionName, packageName) == PackageManager.PERMISSION_GRANTED + } + + // on google version, work only for the current package. + @Suppress("DEPRECATION") + override fun getAppOpMode( + appDesc: ApplicationDescription, + appOpPermissionName: String + ): AppOpModes { + + val appOps = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager + + val mode = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + appOps.checkOpNoThrow(appOpPermissionName, + + appDesc.uid, appDesc.packageName) + } else { + appOps.unsafeCheckOpNoThrow( + appOpPermissionName, + appDesc.uid, appDesc.packageName) + } + + return AppOpModes.getByModeValue(mode) + } + + override fun isPermissionsDangerous(permissionName: String): Boolean { + try { + val permissionInfo = context.packageManager.getPermissionInfo(permissionName, 0) + return isPermissionsDangerous(permissionInfo) + } catch (e: Exception) { + Log.w(TAG, "exception in isPermissionsDangerous(String)", e) + return false + } + } + + @Suppress("DEPRECATION") + private fun isPermissionsDangerous(permissionInfo: PermissionInfo): Boolean { + try { + return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { + permissionInfo.protectionLevel and PROTECTION_DANGEROUS == 1 + } else { + permissionInfo.protection == PROTECTION_DANGEROUS + } + } catch (e: Exception) { + Log.w(TAG, "exception in isPermissionsDangerous(PermissionInfo)", e) + return false + } + } + + override fun buildApplicationDescription(appInfo: ApplicationInfo, withIcon: Boolean) + : ApplicationDescription { + return ApplicationDescription( + packageName = appInfo.packageName, + uid = appInfo.uid, + label = getAppLabel(appInfo), + icon = if (withIcon) getApplicationIcon(appInfo.packageName) else null + ) + } + + private fun getAppLabel(appInfo: ApplicationInfo): CharSequence { + return context.packageManager.getApplicationLabel(appInfo) + } + + override fun getApplicationIcon(packageName: String): Drawable? { + return context.packageManager.getApplicationIcon(packageName) + } +} diff --git a/api/src/main/java/foundation/e/privacymodules/permissions/IPermissionsPrivacyModule.kt b/api/src/main/java/foundation/e/privacymodules/permissions/IPermissionsPrivacyModule.kt new file mode 100644 index 0000000..ba85f13 --- /dev/null +++ b/api/src/main/java/foundation/e/privacymodules/permissions/IPermissionsPrivacyModule.kt @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2022 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.privacymodules.permissions + +import android.content.pm.ApplicationInfo +import android.graphics.drawable.Drawable +import foundation.e.privacymodules.permissions.data.AppOpModes +import foundation.e.privacymodules.permissions.data.ApplicationDescription +import foundation.e.privacymodules.permissions.data.PermissionDescription + +/** + * List applications and manage theirs permissions. + */ +interface IPermissionsPrivacyModule { + + fun buildApplicationDescription(appInfo: ApplicationInfo, withIcon: Boolean = true): ApplicationDescription + + /** + * List the installed application on the device which have not the FLAGS_SYSTEM. + * @return list of filled up [ApplicationDescription] + */ + fun getInstalledApplications(): List + + /** + * List all the installed application on the device. + * @return list of filled up [ApplicationDescription] + */ + fun getAllApplications(): List + + /** + * List of permissions names used by an app, specified by its [packageName]. + * @param packageName the appId of the app + * @return the list off permission, in the "android.permission.PERMISSION" format. + */ + fun getPermissions(packageName: String): List + + fun getPermissionDescription(permissionName: String): PermissionDescription + + + /** + * Get the filled up [ApplicationDescription] for the app specified by its [packageName] + * @param packageName the appId of the app + * @return the informations about the app. + */ + fun getApplicationDescription(packageName: String): ApplicationDescription + + /** + * Check if the current runtime permission is granted for the specified app. + * + * @param packageName the packageName of the app + * @param permissionName the name of the permission in "android.permission.PERMISSION" format. + * @return the current status for this permission. + */ + fun isDangerousPermissionGranted(packageName: String, permissionName: String): Boolean + + + /** + * Get the appOps mode for the specified [appOpPermissionName] of the specified application. + * + * @param appDesc the application + * @param appOpPermissionName the AppOps permission name. + * @return mode, as a [AppOpModes] + */ + fun getAppOpMode(appDesc: ApplicationDescription, appOpPermissionName: String): AppOpModes + + /** + * Grant or revoke the specified permission for the specigfied app. + * If their is not enough privileges to get the permission, return the false + * + * @param appDesc the application + * @param permissionName the name of the permission in "android.permission.PERMISSION" format. + * @param grant true grant the permission, false revoke it. + * @return true if the permission is or has just been granted, false if + * user has to do it himself. + */ + fun toggleDangerousPermission( + appDesc: ApplicationDescription, + permissionName: String, + grant: Boolean + ): Boolean + + + /** + * Change the appOp Mode for the specified appOpPermission and application. + * @param appDesc the application + * @param appOpPermissionName the AppOps permission name. + * @return true if the mode has been changed, false if + * user has to do it himself. + */ + fun setAppOpMode( + appDesc: ApplicationDescription, + appOpPermissionName: String, + status: AppOpModes + ): Boolean + + /** + * Return true if the application is flagged Dangerous. + */ + fun isPermissionsDangerous(permissionName: String): Boolean + + /** + * Get the application icon. + */ + fun getApplicationIcon(packageName: String): Drawable? + + /** + * Authorize the specified package to be used as Vpn. + * @return true if authorization has been set, false if an error has occurred. + */ + fun setVpnPackageAuthorization(packageName: String): Boolean + +} \ No newline at end of file diff --git a/api/src/main/java/foundation/e/privacymodules/permissions/data/AppOpModes.kt b/api/src/main/java/foundation/e/privacymodules/permissions/data/AppOpModes.kt new file mode 100644 index 0000000..367645d --- /dev/null +++ b/api/src/main/java/foundation/e/privacymodules/permissions/data/AppOpModes.kt @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.privacymodules.permissions.data + +import android.app.AppOpsManager.* +import android.os.Build + +enum class AppOpModes(val modeValue: Int) { + ALLOWED(MODE_ALLOWED), + IGNORED(MODE_IGNORED), + ERRORED(MODE_ERRORED), + DEFAULT(MODE_DEFAULT), + FOREGROUND(if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) MODE_ALLOWED else MODE_FOREGROUND); + + companion object { + private val byMode = mapOf( + FOREGROUND.modeValue to FOREGROUND, + ALLOWED.modeValue to ALLOWED, + IGNORED.modeValue to IGNORED, + ERRORED.modeValue to ERRORED, + DEFAULT.modeValue to DEFAULT, + ) + + fun getByModeValue(modeValue: Int): AppOpModes { + return byMode.get(modeValue) ?: DEFAULT + } + } +} diff --git a/api/src/main/java/foundation/e/privacymodules/permissions/data/ApplicationDescription.kt b/api/src/main/java/foundation/e/privacymodules/permissions/data/ApplicationDescription.kt new file mode 100644 index 0000000..cafe256 --- /dev/null +++ b/api/src/main/java/foundation/e/privacymodules/permissions/data/ApplicationDescription.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2022 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.privacymodules.permissions.data + +import android.graphics.drawable.Drawable + +/** + * Useful informations to identify and describe an application. + */ +data class ApplicationDescription( + val packageName: String, + val uid: Int, + var label: CharSequence?, + var icon: Drawable? +) diff --git a/api/src/main/java/foundation/e/privacymodules/permissions/data/PermissionDescription.kt b/api/src/main/java/foundation/e/privacymodules/permissions/data/PermissionDescription.kt new file mode 100644 index 0000000..9ed297d --- /dev/null +++ b/api/src/main/java/foundation/e/privacymodules/permissions/data/PermissionDescription.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.privacymodules.permissions.data + +data class PermissionDescription( + val name: String, + var isDangerous: Boolean, + val group: String?, + var label: CharSequence?, + var description: CharSequence? +) \ No newline at end of file diff --git a/api/src/main/java/foundation/e/privacymodules/trackers/IBlockTrackersPrivacyModule.kt b/api/src/main/java/foundation/e/privacymodules/trackers/IBlockTrackersPrivacyModule.kt new file mode 100644 index 0000000..53b540e --- /dev/null +++ b/api/src/main/java/foundation/e/privacymodules/trackers/IBlockTrackersPrivacyModule.kt @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2022 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.privacymodules.trackers + + +/** + * Manage trackers blocking and whitelisting. + */ +interface IBlockTrackersPrivacyModule { + + + /** + * Get the state of the blockin module + * @return true when blocking is enabled, false otherwise. + */ + fun isBlockingEnabled(): Boolean + + /** + * Enable blocking, using the previously configured whitelists + */ + fun enableBlocking() + + /** + * Disable blocking + */ + fun disableBlocking() + + /** + * Set or unset in whitelist the App with the specified uid. + * @param appUid the uid of the app + * @param isWhiteListed true, the app will appears in whitelist, false, it won't + */ + fun setWhiteListed(appUid: Int, isWhiteListed: Boolean) + + /** + * Set or unset in whitelist the specifid tracked, for the App specified by its uid. + * @param tracker the tracker + * @param appUid the uid of the app + * @param isWhiteListed true, the app will appears in whitelist, false, it won't + */ + fun setWhiteListed(tracker: Tracker, appUid: Int, isWhiteListed: Boolean) + + /** + * Return true if nothing has been added to the whitelist : everything is blocked. + */ + fun isWhiteListEmpty(): Boolean + + /** + * Return the white listed App, by their UID + */ + fun getWhiteListedApp(): List + + /** + * Return true if the App is whitelisted for trackers blocking. + */ + fun isWhitelisted(appUid: Int): Boolean + + + /** + * List the white listed trackers for an App specified by it uid + */ + fun getWhiteList(appUid: Int): List + + /** + * Callback interface to get updates about the state of the Block trackers module. + */ + interface Listener { + + /** + * Called when the trackers blocking is activated or deactivated. + * @param isBlocking true when activated, false otherwise. + */ + fun onBlockingToggle(isBlocking: Boolean) + } + + fun addListener(listener: Listener) + + fun removeListener(listener: Listener) + + fun clearListeners() +} diff --git a/api/src/main/java/foundation/e/privacymodules/trackers/IDNSBlocker.kt b/api/src/main/java/foundation/e/privacymodules/trackers/IDNSBlocker.kt new file mode 100644 index 0000000..a132aef --- /dev/null +++ b/api/src/main/java/foundation/e/privacymodules/trackers/IDNSBlocker.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.privacymodules.trackers + +interface IDNSBlocker { + companion object { + const val DUMMY_APP_UID = -1 + } + + fun shouldBlock(hostname: String, appUid: Int): Boolean +} diff --git a/api/src/main/java/foundation/e/privacymodules/trackers/ITrackTrackersPrivacyModule.kt b/api/src/main/java/foundation/e/privacymodules/trackers/ITrackTrackersPrivacyModule.kt new file mode 100644 index 0000000..139290e --- /dev/null +++ b/api/src/main/java/foundation/e/privacymodules/trackers/ITrackTrackersPrivacyModule.kt @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2022 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.privacymodules.trackers + +/** + * Get reporting about trackers calls. + */ +interface ITrackTrackersPrivacyModule { + + fun start(trackers: List, enableNotification: Boolean = true) + + /** + * List all the trackers encountered for a specific app. + */ + fun getTrackersForApp(appUid: Int): List + + /** + * Return the number of encountered trackers since "ever" + */ + fun getTrackersCount(): Int + + /** + * Return the number of encountere trackers since "ever", for each app uid. + */ + fun getTrackersCountByApp(): Map + + /** + * Return the number of encountered trackers for the last 24 hours + */ + fun getPastDayTrackersCount(): Int + + /** + * Return the number of encountered trackers for the last month + */ + fun getPastMonthTrackersCount(): Int + + /** + * Return the number of encountered trackers for the last year + */ + fun getPastYearTrackersCount(): Int + + + /** + * Return number of trackers calls by hours, for the last 24hours. + * @return list of 24 numbers of trackers calls by hours + */ + fun getPastDayTrackersCalls(): List> + + /** + * Return number of trackers calls by day, for the last 30 days. + * @return list of 30 numbers of trackers calls by day + */ + fun getPastMonthTrackersCalls(): List> + + /** + * Return number of trackers calls by month, for the last 12 month. + * @return list of 12 numbers of trackers calls by month + */ + fun getPastYearTrackersCalls(): List> + + fun getPastDayTrackersCallsByApps(): Map> + + fun getPastDayTrackersCallsForApp(appUId: Int): Pair + + fun getPastDayMostLeakedApp(): Int + + interface Listener { + + /** + * Called when a new tracker attempt is logged. Consumer may choose to call other methods + * to refresh the data. + */ + fun onNewData() + } + + fun addListener(listener: Listener) + + fun removeListener(listener: Listener) + + fun clearListeners() +} \ No newline at end of file diff --git a/api/src/main/java/foundation/e/privacymodules/trackers/Tracker.kt b/api/src/main/java/foundation/e/privacymodules/trackers/Tracker.kt new file mode 100644 index 0000000..0a4395a --- /dev/null +++ b/api/src/main/java/foundation/e/privacymodules/trackers/Tracker.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2022 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.privacymodules.trackers + +/** + * Describe a tracker. + */ +data class Tracker( + val id: String, + val hostnames: Set, + val label: String, + val exodusId: String? +) diff --git a/app/build.gradle b/app/build.gradle index 5f2b302..61ee623 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,7 +16,10 @@ android { testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - manifestPlaceholders = [ persistent: "false" ] + manifestPlaceholders = [ + persistent: "false", + mainActivityIntentFilterCategory: "android.intent.category.LAUNCHER" + ] resValue("string", "mapbox_key", MAPBOX_KEY) } @@ -47,24 +50,36 @@ android { dimension 'os' minSdkVersion 29 targetSdkVersion 29 + signingConfig signingConfigs.eDebug } e30 { dimension 'os' minSdkVersion 30 targetSdkVersion 30 + signingConfig signingConfigs.eDebug + } + standalone { + dimension 'os' + applicationIdSuffix '.standalone' + minSdkVersion 26 + targetSdkVersion 31 + manifestPlaceholders = [ + persistent: "false", + mainActivityIntentFilterCategory: "android.intent.category.LAUNCHER" + ] + signingConfig signingConfigs.debug } -// google { -// applicationIdSuffix '.google' -// dimension 'os' -// } } buildTypes { debug { - signingConfig null // Set signing config to null as we use signingConfig per variant. + signingConfig null // Set signing config to null as we use signingConfig per variant. } release { - manifestPlaceholders = [ persistent: "true" ] + manifestPlaceholders = [ + persistent: "true", + mainActivityIntentFilterCategory: "android.intent.category.INFO" + ] minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' @@ -78,13 +93,6 @@ android { variant.outputs.all { output -> outputFileName = "Advanced_Privacy-${variant.versionName}-${variant.getFlavorName()}-${variant.buildType.name}.apk" } - if (variant.buildType.name == "debug") { - if (variant.getFlavorName() == "e29" || variant.getFlavorName() == "e30") { - variant.mergedFlavor.signingConfig = signingConfigs.eDebug - } else { - variant.mergedFlavor.signingConfig = signingConfigs.debug - } - } } compileOptions { @@ -103,53 +111,51 @@ android { } dependencies { + implementation project(':api') - compileOnly files('libs/e-ui-sdk-1.0.1-q.jar') - implementation files('libs/lineage-sdk.jar') - // include the google specific version of the modules, just for the google flavor - //googleImplementation project(":privacymodulesgoogle") - // include the e specific version of the modules, just for the e flavor - - implementation 'foundation.e:privacymodule.trackerfilter:0.7.0' - implementation 'foundation.e:privacymodule.api:1.1.0' - e29Implementation 'foundation.e:privacymodule.e-29:0.4.3' - e30Implementation 'foundation.e:privacymodule.e-30:0.4.3' - implementation 'foundation.e:privacymodule.tor:0.2.4' - + standaloneImplementation project(':permissionsstandalone') + e29Implementation('foundation.e:privacymodule.e-29:1.2.0') { + exclude group: 'foundation.e', module: 'privacymodule.api' + } + e30Implementation('foundation.e:privacymodule.e-30:1.2.0') { + exclude group: 'foundation.e', module: 'privacymodule.api' + } - // implementation Libs.Kotlin.stdlib - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$Versions.kotlin" -// implementation Libs.AndroidX.coreKtx - implementation "androidx.core:core-ktx:1.8.0" - -// implementation Libs.AndroidX.Fragment.fragmentKtx - implementation "androidx.fragment:fragment-ktx:$Versions.fragment" + implementation project(':fakelocation') - implementation 'androidx.appcompat:appcompat:1.4.2' -// implementation Libs.AndroidX.Lifecycle.runtime - implementation "androidx.lifecycle:lifecycle-runtime-ktx:$Versions.lifecycle" -// implementation Libs.AndroidX.Lifecycle.viewmodel - implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$Versions.lifecycle" + e29CompileOnly files('libs/e-ui-sdk-1.0.1-q.jar') + e29Implementation files('libs/lineage-sdk.jar') - implementation 'androidx.work:work-runtime-ktx:2.7.1' + e30CompileOnly files('libs/e-ui-sdk-1.0.1-q.jar') + e30Implementation files('libs/lineage-sdk.jar') - implementation 'com.google.android.material:material:1.6.1' + implementation project(':trackers') + implementation 'foundation.e:privacymodule.tor:0.2.4' - implementation 'com.squareup.retrofit2:retrofit:2.9.0' - implementation 'com.squareup.retrofit2:converter-scalars:2.9.0' + implementation ( + Libs.Kotlin.stdlib, + Libs.AndroidX.coreKtx, + Libs.AndroidX.appCompat, + Libs.AndroidX.Fragment.fragmentKtx, + Libs.AndroidX.Lifecycle.runtime, + Libs.AndroidX.Lifecycle.viewmodel, + Libs.AndroidX.work, + Libs.material, -// implementation Libs.MapBox.sdk - implementation "com.mapbox.mapboxsdk:mapbox-android-sdk:$Versions.mapbox" - implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' + Libs.Retrofit.retrofit, + Libs.Retrofit.scalars, + Libs.MapBox.sdk, + Libs.mpAndroidCharts + ) + debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' - debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1' } static def log(Object val) { diff --git a/app/src/google/res/values/strings.xml b/app/src/google/res/values/strings.xml deleted file mode 100644 index ebf51d0..0000000 --- a/app/src/google/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - google - PrivacyModulesDemo - diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d285b6f..d2a824a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,4 +1,20 @@ + @@ -53,19 +70,28 @@ android:resource="@xml/widget_info" /> - + + android:launchMode="singleTask" + android:exported="true" + > - + + + \ No newline at end of file diff --git a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt index 6be3724..a44a00a 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/DependencyContainer.kt @@ -40,16 +40,15 @@ import foundation.e.privacycentralapp.features.location.FakeLocationViewModel import foundation.e.privacycentralapp.features.trackers.TrackersViewModel import foundation.e.privacycentralapp.features.trackers.apptrackers.AppTrackersFragment import foundation.e.privacycentralapp.features.trackers.apptrackers.AppTrackersViewModel +import foundation.e.privacymodules.fakelocation.FakeLocationModule import foundation.e.privacymodules.ipscrambler.IpScramblerModule import foundation.e.privacymodules.ipscramblermodule.IIpScramblerModule -import foundation.e.privacymodules.location.FakeLocationModule import foundation.e.privacymodules.location.IFakeLocationModule import foundation.e.privacymodules.permissions.PermissionsPrivacyModule import foundation.e.privacymodules.permissions.data.ApplicationDescription import foundation.e.privacymodules.trackers.api.BlockTrackersPrivacyModule import foundation.e.privacymodules.trackers.api.TrackTrackersPrivacyModule import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.GlobalScope /** @@ -61,7 +60,7 @@ class DependencyContainer(val app: Application) { val context: Context by lazy { app.applicationContext } // Drivers - private val fakeLocationModule: IFakeLocationModule by lazy { FakeLocationModule(app.applicationContext) } + private val fakeLocationModule: FakeLocationModule by lazy { FakeLocationModule(app.applicationContext) } private val permissionsModule by lazy { PermissionsPrivacyModule(app.applicationContext) } private val ipScramblerModule: IIpScramblerModule by lazy { IpScramblerModule(app.applicationContext) } diff --git a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/FakeLocationStateUseCase.kt b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/FakeLocationStateUseCase.kt index aa4276d..f7b5439 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/FakeLocationStateUseCase.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/domain/usecases/FakeLocationStateUseCase.kt @@ -28,7 +28,7 @@ import android.util.Log import foundation.e.privacycentralapp.data.repositories.LocalStateRepository import foundation.e.privacycentralapp.domain.entities.LocationMode import foundation.e.privacycentralapp.dummy.CityDataSource -import foundation.e.privacymodules.location.IFakeLocationModule +import foundation.e.privacymodules.fakelocation.FakeLocationModule import foundation.e.privacymodules.permissions.PermissionsPrivacyModule import foundation.e.privacymodules.permissions.data.AppOpModes import foundation.e.privacymodules.permissions.data.ApplicationDescription @@ -39,7 +39,7 @@ import kotlinx.coroutines.launch import kotlin.random.Random class FakeLocationStateUseCase( - private val fakeLocationModule: IFakeLocationModule, + private val fakeLocationModule: FakeLocationModule, private val permissionsModule: PermissionsPrivacyModule, private val localStateRepository: LocalStateRepository, private val citiesRepository: CityDataSource, @@ -61,23 +61,15 @@ class FakeLocationStateUseCase( private val locationManager: LocationManager get() = appContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager - private fun acquireLocationPermission() { - if (appContext.checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { - permissionsModule.toggleDangerousPermission( - appDesc, - android.Manifest.permission.ACCESS_FINE_LOCATION, - true - ) - } + private fun hasAcquireLocationPermission(): Boolean { + return (appContext.checkSelfPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) + || permissionsModule.toggleDangerousPermission(appDesc, android.Manifest.permission.ACCESS_FINE_LOCATION, true) } private fun applySettings(isQuickPrivacyEnabled: Boolean, fakeLocation: Pair?, isSpecificLocation: Boolean = false) { _configuredLocationMode.value = computeLocationMode(fakeLocation, isSpecificLocation) - if (isQuickPrivacyEnabled && fakeLocation != null) { - if (permissionsModule.getAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION) != AppOpModes.ALLOWED) { - permissionsModule.setAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION, AppOpModes.ALLOWED) - } + if (isQuickPrivacyEnabled && fakeLocation != null && hasAcquireMockLocationPermission()) { fakeLocationModule.startFakeLocation() fakeLocationModule.setFakeLocation(fakeLocation.first.toDouble(), fakeLocation.second.toDouble()) localStateRepository.locationMode.value = configuredLocationMode.value.first @@ -87,6 +79,11 @@ class FakeLocationStateUseCase( } } + private fun hasAcquireMockLocationPermission(): Boolean { + return (permissionsModule.getAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION) == AppOpModes.ALLOWED) + || permissionsModule.setAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION, AppOpModes.ALLOWED) + } + fun setSpecificLocation(latitude: Float, longitude: Float) { if (!localStateRepository.isQuickPrivacyEnabled) { localStateRepository.setShowQuickPrivacyDisabledMessage(true) @@ -161,8 +158,11 @@ class FakeLocationStateUseCase( } } - fun startListeningLocation() { - requestLocationUpdates(localListener) + fun startListeningLocation(): Boolean { + return if (hasAcquireLocationPermission()) { + requestLocationUpdates(localListener) + true + } else false } fun stopListeningLocation() { @@ -170,7 +170,6 @@ class FakeLocationStateUseCase( } fun requestLocationUpdates(listener: LocationListener) { - acquireLocationPermission() try { locationManager.requestLocationUpdates( LocationManager.NETWORK_PROVIDER, // TODO: tight this with fakelocation module. diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt index 2b858e9..d98cb5d 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationFragment.kt @@ -17,6 +17,7 @@ package foundation.e.privacycentralapp.features.location +import android.Manifest import android.annotation.SuppressLint import android.content.Context import android.location.Location @@ -24,6 +25,7 @@ import android.os.Bundle import android.text.Editable import android.view.View import android.widget.Toast +import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.NonNull import androidx.core.view.isVisible import androidx.core.widget.addTextChangedListener @@ -81,6 +83,16 @@ class FakeLocationFragment : NavToolbarFragment(R.layout.fragment_fake_location) private var inputJob: Job? = null + private val locationPermissionRequest = registerForActivityResult( + ActivityResultContracts.RequestMultiplePermissions() + ) { permissions -> + if (permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) + || permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) + ) { + viewModel.submitAction(Action.StartListeningLocation) + } // TODO: else. + } + companion object { private const val DEBOUNCE_PERIOD = 1000L } @@ -147,6 +159,13 @@ class FakeLocationFragment : NavToolbarFragment(R.layout.fragment_fake_location) is FakeLocationViewModel.SingleEvent.LocationUpdatedEvent -> { updateLocation(event.location, event.mode) } + is FakeLocationViewModel.SingleEvent.RequestLocationPermission -> { + // TODO for standalone: rationale dialog + locationPermissionRequest.launch(arrayOf( + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + )) + } } } } @@ -326,13 +345,13 @@ class FakeLocationFragment : NavToolbarFragment(R.layout.fragment_fake_location) override fun onResume() { super.onResume() - viewModel.submitAction(Action.EnterScreen) + viewModel.submitAction(Action.StartListeningLocation) binding.mapView.onResume() } override fun onPause() { super.onPause() - viewModel.submitAction(Action.LeaveScreen) + viewModel.submitAction(Action.StopListeningLocation) binding.mapView.onPause() } diff --git a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationViewModel.kt b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationViewModel.kt index af20a72..afba3d0 100644 --- a/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationViewModel.kt +++ b/app/src/main/java/foundation/e/privacycentralapp/features/location/FakeLocationViewModel.kt @@ -86,8 +86,8 @@ class FakeLocationViewModel( fun submitAction(action: Action) = viewModelScope.launch { when (action) { - is Action.EnterScreen -> fakeLocationStateUseCase.startListeningLocation() - is Action.LeaveScreen -> fakeLocationStateUseCase.stopListeningLocation() + is Action.StartListeningLocation -> actionStartListeningLocation() + is Action.StopListeningLocation -> fakeLocationStateUseCase.stopListeningLocation() is Action.SetSpecificLocationAction -> setSpecificLocation(action) is Action.UseRandomLocationAction -> fakeLocationStateUseCase.setRandomLocation() is Action.UseRealLocationAction -> @@ -97,18 +97,26 @@ class FakeLocationViewModel( } } + private suspend fun actionStartListeningLocation() { + val started = fakeLocationStateUseCase.startListeningLocation() + if (!started) { + _singleEvents.emit(SingleEvent.RequestLocationPermission) + } + } + private suspend fun setSpecificLocation(action: Action.SetSpecificLocationAction) { specificLocationInputFlow.emit(action) } sealed class SingleEvent { data class LocationUpdatedEvent(val mode: LocationMode, val location: Location?) : SingleEvent() + object RequestLocationPermission: SingleEvent() data class ErrorEvent(val error: String) : SingleEvent() } sealed class Action { - object EnterScreen : Action() - object LeaveScreen : Action() + object StartListeningLocation : Action() + object StopListeningLocation : Action() object UseRealLocationAction : Action() object UseRandomLocationAction : Action() data class SetSpecificLocationAction( diff --git a/app/src/standalone/res/values-night/colors.xml b/app/src/standalone/res/values-night/colors.xml new file mode 100644 index 0000000..079b968 --- /dev/null +++ b/app/src/standalone/res/values-night/colors.xml @@ -0,0 +1,28 @@ + + + + + #272727 + #5DB2FF + + #CCFFFFFF + #8CFFFFFF + + #121212 + + \ No newline at end of file diff --git a/app/src/standalone/res/values/colors.xml b/app/src/standalone/res/values/colors.xml new file mode 100644 index 0000000..bd27922 --- /dev/null +++ b/app/src/standalone/res/values/colors.xml @@ -0,0 +1,27 @@ + + + + + #FFFFFF + #0086FF + + #CC000000 + #8C000000 + + #FAFAFA + \ No newline at end of file diff --git a/app/src/standalone/res/values/strings.xml b/app/src/standalone/res/values/strings.xml new file mode 100644 index 0000000..7719e7b --- /dev/null +++ b/app/src/standalone/res/values/strings.xml @@ -0,0 +1,3 @@ + + A-P - Standalone + diff --git a/build.gradle b/build.gradle index 2442f01..5222057 100644 --- a/build.gradle +++ b/build.gradle @@ -6,11 +6,11 @@ buildscript { ext.buildConfig = [ 'compileSdk': 31, 'minSdk' : 26, - 'targetSdk' : 30, + 'targetSdk' : 31, 'version' : [ 'major': 1, - 'minor': 1, - 'patch': 2, + 'minor': 2, + 'patch': 0, ], ] @@ -31,7 +31,7 @@ buildscript { dependencies { classpath Libs.androidGradlePlugin - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10" + classpath Libs.Kotlin.gradlePlugin // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -41,6 +41,7 @@ buildscript { plugins { id 'com.diffplug.spotless' version '5.12.4' id 'com.github.ben-manes.versions' version '0.38.0' + id 'org.jetbrains.kotlin.android' version '1.6.10' apply false } allprojects { @@ -49,7 +50,6 @@ allprojects { kotlinOptions { freeCompilerArgs = ['-Xjvm-default=enable', '-opt-in=kotlin.RequiresOptIn'] - jvmTarget = "1.8" } } diff --git a/dependencies.gradle b/dependencies.gradle index dcb9f9d..ed329c7 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -6,7 +6,7 @@ def versions = [ ] ext.Versions = versions -libs.androidGradlePlugin = "com.android.tools.build:gradle:4.1.3" +libs.androidGradlePlugin = "com.android.tools.build:gradle:7.2.1" libs.timber = "com.jakewharton.timber:timber:4.7.1" @@ -25,7 +25,9 @@ libs.Kotlin = [ gradlePlugin: "org.jetbrains.kotlin:kotlin-gradle-plugin:$versions.kotlin", ] -versions.coroutines = "1.4.2" + + +versions.coroutines = "1.6.1" libs.Coroutines = [ core: "org.jetbrains.kotlinx:kotlinx-coroutines-core:$versions.coroutines", android: "org.jetbrains.kotlinx:kotlinx-coroutines-android:$versions.coroutines", @@ -36,7 +38,10 @@ libs.AndroidX = [ collection: "androidx.collection:collection-ktx:1.1.0", palette: "androidx.palette:palette:1.0.0", archCoreTesting: "androidx.arch.core:core-testing:2.1.0", - coreKtx: "androidx.core:core-ktx:1.5.0-beta01", + coreKtx: "androidx.core:core-ktx:1.8.0", + coreKtxAPI29: "androidx.core:core-ktx:1.6.0", + appCompat: 'androidx.appcompat:appcompat:1.4.2', + work: 'androidx.work:work-runtime-ktx:2.7.1', ] versions.fragment = "1.5.0" @@ -82,7 +87,17 @@ libs.Hilt = [ gradlePlugin: "com.google.dagger:hilt-android-gradle-plugin:$versions.hilt", ] +libs.material = 'com.google.android.material:material:1.6.1' + +libs.Retrofit = [ + retrofit: 'com.squareup.retrofit2:retrofit:2.9.0', + scalars: 'com.squareup.retrofit2:converter-scalars:2.9.0' +] + + versions.mapbox="9.6.1" libs.MapBox = [ sdk: "com.mapbox.mapboxsdk:mapbox-android-sdk:$versions.mapbox" ] + +libs.mpAndroidCharts = 'com.github.PhilJay:MPAndroidChart:v3.1.0' diff --git a/fakelocation/.gitignore b/fakelocation/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/fakelocation/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/fakelocation/build.gradle b/fakelocation/build.gradle new file mode 100644 index 0000000..ea28e44 --- /dev/null +++ b/fakelocation/build.gradle @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +plugins { + id 'com.android.library' + id 'org.jetbrains.kotlin.android' +} + +android { + compileSdkVersion buildConfig.compileSdk + + defaultConfig { + minSdkVersion buildConfig.minSdk + targetSdkVersion buildConfig.targetSdk + + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + implementation ( + Libs.Kotlin.stdlib, + Libs.AndroidX.coreKtx, + Libs.Coroutines.core + ) +} diff --git a/fakelocation/consumer-rules.pro b/fakelocation/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/fakelocation/fakelocationdemo/.gitignore b/fakelocation/fakelocationdemo/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/fakelocation/fakelocationdemo/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/fakelocation/fakelocationdemo/build.gradle b/fakelocation/fakelocationdemo/build.gradle new file mode 100644 index 0000000..12ed2e7 --- /dev/null +++ b/fakelocation/fakelocationdemo/build.gradle @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2022 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +plugins { + id 'com.android.application' + id 'org.jetbrains.kotlin.android' +} + +android { + compileSdkVersion buildConfig.compileSdk + + defaultConfig { + applicationId "foundation.e.privacymodules.fakelocationdemo" + minSdkVersion buildConfig.minSdk + targetSdkVersion buildConfig.targetSdk + + versionCode buildConfig.version.code + versionName buildConfig.version.name + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + allWarningsAsErrors = false + } + buildFeatures { + dataBinding true + } +} + +dependencies { + implementation project(':api') + implementation project(':fakelocation') + implementation project(':permissionsstandalone') + + + implementation ( + Libs.Kotlin.stdlib, + Libs.AndroidX.coreKtx, + Libs.AndroidX.appCompat, + Libs.material, + ) + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' +} \ No newline at end of file diff --git a/fakelocation/fakelocationdemo/proguard-rules.pro b/fakelocation/fakelocationdemo/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/fakelocation/fakelocationdemo/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/fakelocation/fakelocationdemo/src/main/AndroidManifest.xml b/fakelocation/fakelocationdemo/src/main/AndroidManifest.xml new file mode 100644 index 0000000..202599a --- /dev/null +++ b/fakelocation/fakelocationdemo/src/main/AndroidManifest.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt new file mode 100644 index 0000000..1b0a35b --- /dev/null +++ b/fakelocation/fakelocationdemo/src/main/java/foundation/e/privacymodules/fakelocationdemo/MainActivity.kt @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2022 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 + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.privacymodules.fakelocationdemo + +import android.Manifest +import android.app.AppOpsManager +import android.content.Context +import android.content.pm.PackageManager +import android.location.Location +import android.location.LocationListener +import android.location.LocationManager +import android.os.Bundle +import android.os.Process.myUid +import android.util.Log +import android.view.View +import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import androidx.core.content.ContextCompat +import androidx.databinding.DataBindingUtil +import foundation.e.privacymodules.fakelocation.FakeLocationModule +import foundation.e.privacymodules.fakelocationdemo.databinding.ActivityMainBinding +import foundation.e.privacymodules.permissions.PermissionsPrivacyModule +import foundation.e.privacymodules.permissions.data.AppOpModes +import foundation.e.privacymodules.permissions.data.ApplicationDescription + +class MainActivity : AppCompatActivity() { + companion object { + const val TAG = "fakeLoc" + } + + private val fakeLocationModule: FakeLocationModule by lazy { FakeLocationModule(this) } + private val permissionsModule by lazy { PermissionsPrivacyModule(this) } + + private lateinit var binding: ActivityMainBinding + + private val appDesc by lazy { + ApplicationDescription( + packageName = packageName, + uid = myUid(), + label = getString(R.string.app_name), + icon = null + ) + } + + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = DataBindingUtil.setContentView(this, R.layout.activity_main) + + actionBar?.setDisplayHomeAsUpEnabled(true) + + binding.view = this + } + + override fun onResume() { + super.onResume() + updateData("") + } + + private fun updateData(mockedLocation: String) { + binding.granted = permissionsModule.getAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION) == AppOpModes.ALLOWED + + binding.mockedLocation = mockedLocation + } + + private val listener = object: LocationListener { + override fun onLocationChanged(location: Location) { + binding.currentLocation = "lat: ${location.latitude} - lon: ${location.longitude}" + } + + override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) { + TODO("Not yet implemented") + } + + override fun onProviderEnabled(provider: String) { + binding.providerInfo = "onProdivderEnabled: $provider" + } + + override fun onProviderDisabled(provider: String) { + binding.providerInfo = "onProdivderDisabled: $provider" + } + } + + @Suppress("UNUSED_PARAMETER") + fun onClickToggleListenLocation(view: View?) { + val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager + + if (binding.toggleListenLocation.isChecked) { + requireLocationPermissions() + return + } + + locationManager.removeUpdates(listener) + binding.currentLocation = "no listening" + } + + private fun startLocationUpdates() { + val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager + + try { + Log.d(TAG, "requestLocationUpdates") + locationManager.requestLocationUpdates( + LocationManager.GPS_PROVIDER, // TODO: tight this with fakelocation module. + 1000L, + 1f, + listener + ) + binding.currentLocation = "listening started" + } catch (se: SecurityException) { + Log.e(TAG, "Missing permission", se) + } + } + + private val locationPermissionRequest = registerForActivityResult( + ActivityResultContracts.RequestMultiplePermissions() + ) { permissions -> + if (permissions.getOrDefault(Manifest.permission.ACCESS_FINE_LOCATION, false) + || permissions.getOrDefault(Manifest.permission.ACCESS_COARSE_LOCATION, false) + ) { + startLocationUpdates() + } + } + + private fun requireLocationPermissions() { + if (ContextCompat.checkSelfPermission(this, + Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + startLocationUpdates() + } else { + // Before you perform the actual permission request, check whether your app + // already has the permissions, and whether your app needs to show a permission + // rationale dialog. For more details, see Request permissions. + locationPermissionRequest.launch( + arrayOf( + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + ) + ) + } + + } + + @Suppress("UNUSED_PARAMETER") + fun onClickPermission(view: View?) { + val isGranted = permissionsModule.setAppOpMode(appDesc, AppOpsManager.OPSTR_MOCK_LOCATION, + AppOpModes.ALLOWED) + + if (isGranted) { + updateData("") + return + } + //dev mode disabled + val alertDialog = AlertDialog.Builder(this) + alertDialog + .setTitle("Mock location disabled") + .setNegativeButton("Cancel") { _, _ -> + } + alertDialog.create().show() + } + + @Suppress("UNUSED_PARAMETER") + fun onClickReset(view: View?) { + try { + fakeLocationModule.stopFakeLocation() + } catch(e: Exception) { + Log.e(TAG, "Can't stop FakeLocation", e) + } + } + + private fun setFakeLocation(latitude: Double, longitude: Double) { + try { + fakeLocationModule.startFakeLocation() + } catch(e: Exception) { + Log.e(TAG, "Can't startFakeLocation", e) + } + fakeLocationModule.setFakeLocation(latitude, longitude) + updateData("lat: ${latitude} - lon: ${longitude}") + } + + @Suppress("UNUSED_PARAMETER") + fun onClickParis(view: View?) { + setFakeLocation(48.8502282, 2.3542286) + } + + @Suppress("UNUSED_PARAMETER") + fun onClickLondon(view: View?) { + setFakeLocation(51.5287718, -0.2416803) + } + + @Suppress("UNUSED_PARAMETER") + fun onClickAmsterdam(view: View?) { + setFakeLocation(52.3547498, 4.8339211) + } +} diff --git a/fakelocation/fakelocationdemo/src/main/res/layout/activity_main.xml b/fakelocation/fakelocationdemo/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..33fce69 --- /dev/null +++ b/fakelocation/fakelocationdemo/src/main/res/layout/activity_main.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + +