mirror of
https://git.citron-emu.org/Citron/Citron.git
synced 2025-02-23 17:18:47 +01:00
Add license verification for Android app
Implements a LicenseVerifier class to ensure app integrity and license compliance: - Verifies the app's package name matches the official release - Validates app signature against official release signature - Allows debug and EA (Early Access) builds - Shows violation dialog and exits if verification fails - Enforces GPLv3 license compliance for modified versions This helps prevent unauthorized modified versions from being distributed without source code, as required by the GPLv3 license.
This commit is contained in:
parent
4d50d2ba16
commit
18f8a0f997
7 changed files with 135 additions and 53 deletions
|
@ -98,24 +98,21 @@ endif()
|
||||||
option(ENABLE_OPENSSL "Enable OpenSSL backend for ISslConnection" ${DEFAULT_ENABLE_OPENSSL})
|
option(ENABLE_OPENSSL "Enable OpenSSL backend for ISslConnection" ${DEFAULT_ENABLE_OPENSSL})
|
||||||
|
|
||||||
if (ANDROID AND CITRON_DOWNLOAD_ANDROID_VVL)
|
if (ANDROID AND CITRON_DOWNLOAD_ANDROID_VVL)
|
||||||
set(vvl_version "vulkan-sdk-1.4.304.1")
|
set(vvl_version "1.4.304.1")
|
||||||
set(vvl_zip_file "${CMAKE_BINARY_DIR}/externals/vvl-android.zip")
|
set(vvl_zip_file "${CMAKE_BINARY_DIR}/externals/vvl-android.zip")
|
||||||
if (NOT EXISTS "${vvl_zip_file}")
|
if (NOT EXISTS "${vvl_zip_file}")
|
||||||
# Download and extract validation layer release to externals directory
|
# Download and extract validation layer release to externals directory
|
||||||
set(vvl_base_url "https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download")
|
set(vvl_base_url "https://github.com/KhronosGroup/Vulkan-ValidationLayers/releases/download")
|
||||||
file(DOWNLOAD "${vvl_base_url}/${vvl_version}/android-binaries-1.4.304.1.zip"
|
file(DOWNLOAD "${vvl_base_url}/vulkan-sdk-${vvl_version}/android-binaries-${vvl_version}.zip"
|
||||||
"${vvl_zip_file}" SHOW_PROGRESS)
|
"${vvl_zip_file}" SHOW_PROGRESS)
|
||||||
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${vvl_zip_file}"
|
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf "${vvl_zip_file}"
|
||||||
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/externals")
|
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/externals")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Copy the arm64 binary to src/android/app/main/jniLibs only if it doesn't exist
|
# Copy the arm64 binary to src/android/app/main/jniLibs
|
||||||
set(vvl_lib_path "${CMAKE_CURRENT_SOURCE_DIR}/src/android/app/src/main/jniLibs/arm64-v8a/")
|
set(vvl_lib_path "${CMAKE_CURRENT_SOURCE_DIR}/src/android/app/src/main/jniLibs/arm64-v8a/")
|
||||||
set(vvl_lib_file "${vvl_lib_path}/libVkLayer_khronos_validation.so")
|
|
||||||
if (NOT EXISTS "${vvl_lib_file}")
|
|
||||||
file(COPY "${CMAKE_BINARY_DIR}/externals/android-binaries-${vvl_version}/arm64-v8a/libVkLayer_khronos_validation.so"
|
file(COPY "${CMAKE_BINARY_DIR}/externals/android-binaries-${vvl_version}/arm64-v8a/libVkLayer_khronos_validation.so"
|
||||||
DESTINATION "${vvl_lib_path}")
|
DESTINATION "${vvl_lib_path}")
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (ANDROID)
|
if (ANDROID)
|
||||||
|
@ -129,15 +126,22 @@ if (CITRON_USE_BUNDLED_VCPKG)
|
||||||
|
|
||||||
if (CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a")
|
if (CMAKE_ANDROID_ARCH_ABI STREQUAL "arm64-v8a")
|
||||||
set(VCPKG_TARGET_TRIPLET "arm64-android")
|
set(VCPKG_TARGET_TRIPLET "arm64-android")
|
||||||
|
set(VCPKG_HOST_TRIPLET "x64-windows")
|
||||||
# this is to avoid CMake using the host pkg-config to find the host
|
# this is to avoid CMake using the host pkg-config to find the host
|
||||||
# libraries when building for Android targets
|
# libraries when building for Android targets
|
||||||
set(PKG_CONFIG_EXECUTABLE "aarch64-none-linux-android-pkg-config" CACHE FILEPATH "" FORCE)
|
set(PKG_CONFIG_EXECUTABLE "aarch64-none-linux-android-pkg-config" CACHE FILEPATH "" FORCE)
|
||||||
elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL "x86_64")
|
elseif (CMAKE_ANDROID_ARCH_ABI STREQUAL "x86_64")
|
||||||
set(VCPKG_TARGET_TRIPLET "x64-android")
|
set(VCPKG_TARGET_TRIPLET "x64-android")
|
||||||
|
set(VCPKG_HOST_TRIPLET "x64-windows")
|
||||||
set(PKG_CONFIG_EXECUTABLE "x86_64-none-linux-android-pkg-config" CACHE FILEPATH "" FORCE)
|
set(PKG_CONFIG_EXECUTABLE "x86_64-none-linux-android-pkg-config" CACHE FILEPATH "" FORCE)
|
||||||
else()
|
else()
|
||||||
message(FATAL_ERROR "Unsupported Android architecture ${CMAKE_ANDROID_ARCH_ABI}")
|
message(FATAL_ERROR "Unsupported Android architecture ${CMAKE_ANDROID_ARCH_ABI}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Add these lines to ensure proper Android toolchain setup
|
||||||
|
set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE "${ANDROID_NDK}/build/cmake/android.toolchain.cmake")
|
||||||
|
set(VCPKG_CRT_LINKAGE "dynamic")
|
||||||
|
set(VCPKG_LIBRARY_LINKAGE "static")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
|
|
2
externals/vcpkg
vendored
2
externals/vcpkg
vendored
|
@ -1 +1 @@
|
||||||
Subproject commit 33e9c99208736b713cabe4490e15235f62f893d4
|
Subproject commit 37d46edf0f2024c3d04997a2d432d59278ca1dff
|
|
@ -1,8 +1,9 @@
|
||||||
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
|
||||||
// SPDX-FileCopyrightText: 2025 citron Emulator Project
|
// SPDX-FileCopyrightText: 2023 citron Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import kotlin.collections.setOf
|
||||||
import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
|
import org.jlleitschuh.gradle.ktlint.reporter.ReporterType
|
||||||
import com.github.triplet.gradle.androidpublisher.ReleaseStatus
|
import com.github.triplet.gradle.androidpublisher.ReleaseStatus
|
||||||
|
|
||||||
|
@ -26,22 +27,21 @@ val autoVersion = (((System.currentTimeMillis() / 1000) - 1451606400) / 10).toIn
|
||||||
@Suppress("UnstableApiUsage")
|
@Suppress("UnstableApiUsage")
|
||||||
android {
|
android {
|
||||||
namespace = "org.citron.citron_emu"
|
namespace = "org.citron.citron_emu"
|
||||||
compileSdk = 35
|
|
||||||
|
|
||||||
ndkVersion = "28.0.13004108" // "26.3.11579264"
|
compileSdkVersion = "android-34"
|
||||||
|
ndkVersion = "26.1.10909125"
|
||||||
|
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding = true
|
viewBinding = true
|
||||||
buildConfig = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility = JavaVersion.VERSION_21
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
targetCompatibility = JavaVersion.VERSION_21
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlinOptions {
|
kotlinOptions {
|
||||||
jvmTarget = "21"
|
jvmTarget = "17"
|
||||||
}
|
}
|
||||||
|
|
||||||
packaging {
|
packaging {
|
||||||
|
@ -57,7 +57,7 @@ android {
|
||||||
// TODO If this is ever modified, change application_id in strings.xml
|
// TODO If this is ever modified, change application_id in strings.xml
|
||||||
applicationId = "org.citron.citron_emu"
|
applicationId = "org.citron.citron_emu"
|
||||||
minSdk = 30
|
minSdk = 30
|
||||||
targetSdk = 35
|
targetSdk = 34
|
||||||
versionName = getGitVersion()
|
versionName = getGitVersion()
|
||||||
|
|
||||||
versionCode = if (System.getenv("AUTO_VERSIONED") == "true") {
|
versionCode = if (System.getenv("AUTO_VERSIONED") == "true") {
|
||||||
|
@ -75,6 +75,15 @@ android {
|
||||||
buildConfigField("String", "BRANCH", "\"${getBranch()}\"")
|
buildConfigField("String", "BRANCH", "\"${getBranch()}\"")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
android.applicationVariants.all {
|
||||||
|
val variant = this
|
||||||
|
variant.outputs.all {
|
||||||
|
if (this is com.android.build.gradle.internal.api.ApkVariantOutputImpl) {
|
||||||
|
outputFileName = "Citron-${variant.versionName}-${variant.name}.apk"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val keystoreFile = System.getenv("ANDROID_KEYSTORE_FILE")
|
val keystoreFile = System.getenv("ANDROID_KEYSTORE_FILE")
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
if (keystoreFile != null) {
|
if (keystoreFile != null) {
|
||||||
|
@ -106,12 +115,10 @@ android {
|
||||||
|
|
||||||
resValue("string", "app_name_suffixed", "Citron")
|
resValue("string", "app_name_suffixed", "Citron")
|
||||||
isDefault = true
|
isDefault = true
|
||||||
isShrinkResources = true
|
|
||||||
isMinifyEnabled = true
|
isMinifyEnabled = true
|
||||||
isJniDebuggable = false
|
|
||||||
isDebuggable = false
|
isDebuggable = false
|
||||||
proguardFiles(
|
proguardFiles(
|
||||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
getDefaultProguardFile("proguard-android.txt"),
|
||||||
"proguard-rules.pro"
|
"proguard-rules.pro"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -121,10 +128,9 @@ android {
|
||||||
register("relWithDebInfo") {
|
register("relWithDebInfo") {
|
||||||
resValue("string", "app_name_suffixed", "Citron Debug Release")
|
resValue("string", "app_name_suffixed", "Citron Debug Release")
|
||||||
signingConfig = signingConfigs.getByName("default")
|
signingConfig = signingConfigs.getByName("default")
|
||||||
isMinifyEnabled = true
|
|
||||||
isDebuggable = true
|
isDebuggable = true
|
||||||
proguardFiles(
|
proguardFiles(
|
||||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
getDefaultProguardFile("proguard-android.txt"),
|
||||||
"proguard-rules.pro"
|
"proguard-rules.pro"
|
||||||
)
|
)
|
||||||
versionNameSuffix = "-relWithDebInfo"
|
versionNameSuffix = "-relWithDebInfo"
|
||||||
|
@ -165,7 +171,6 @@ android {
|
||||||
path = file("../../../CMakeLists.txt")
|
path = file("../../../CMakeLists.txt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buildToolsVersion = "35.0.1"
|
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
|
@ -183,14 +188,14 @@ android {
|
||||||
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
|
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
|
||||||
)
|
)
|
||||||
|
|
||||||
abiFilters("arm64-v8a") // , "x86_64")
|
abiFilters("arm64-v8a", "x86_64")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.create<Delete>("ktlintReset") {
|
tasks.create<Delete>("ktlintReset") {
|
||||||
delete(File(layout.buildDirectory.toString() + File.separator + "intermediates/ktLint"))
|
delete(File(buildDir.path + File.separator + "intermediates/ktLint"))
|
||||||
}
|
}
|
||||||
|
|
||||||
val showFormatHelp = {
|
val showFormatHelp = {
|
||||||
|
@ -207,6 +212,13 @@ ktlint {
|
||||||
version.set("0.47.1")
|
version.set("0.47.1")
|
||||||
android.set(true)
|
android.set(true)
|
||||||
ignoreFailures.set(false)
|
ignoreFailures.set(false)
|
||||||
|
disabledRules.set(
|
||||||
|
setOf(
|
||||||
|
"no-wildcard-imports",
|
||||||
|
"package-name",
|
||||||
|
"import-ordering"
|
||||||
|
)
|
||||||
|
)
|
||||||
reporters {
|
reporters {
|
||||||
reporter(ReporterType.CHECKSTYLE)
|
reporter(ReporterType.CHECKSTYLE)
|
||||||
}
|
}
|
||||||
|
@ -222,36 +234,24 @@ play {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// AndroidX Core & UI
|
|
||||||
implementation("androidx.core:core-ktx:1.12.0")
|
implementation("androidx.core:core-ktx:1.12.0")
|
||||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
|
||||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
|
||||||
implementation("androidx.recyclerview:recyclerview:1.3.1")
|
implementation("androidx.recyclerview:recyclerview:1.3.1")
|
||||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||||
implementation("androidx.window:window:1.2.0-beta03")
|
implementation("androidx.fragment:fragment-ktx:1.6.1")
|
||||||
|
implementation("androidx.documentfile:documentfile:1.0.1")
|
||||||
implementation("com.google.android.material:material:1.9.0")
|
implementation("com.google.android.material:material:1.9.0")
|
||||||
|
implementation("androidx.preference:preference-ktx:1.2.1")
|
||||||
// AndroidX Navigation
|
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
|
||||||
|
implementation("io.coil-kt:coil:2.2.2")
|
||||||
|
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||||
|
implementation("androidx.window:window:1.2.0-beta03")
|
||||||
|
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||||
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
|
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
|
||||||
implementation("androidx.navigation:navigation-ui-ktx:2.7.4")
|
implementation("androidx.navigation:navigation-ui-ktx:2.7.4")
|
||||||
|
|
||||||
// AndroidX Lifecycle
|
|
||||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
|
|
||||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
|
|
||||||
|
|
||||||
// AndroidX Other
|
|
||||||
implementation("androidx.documentfile:documentfile:1.0.1")
|
|
||||||
implementation("androidx.fragment:fragment-ktx:1.6.2")
|
|
||||||
implementation("androidx.preference:preference-ktx:1.2.1")
|
|
||||||
|
|
||||||
// Kotlin
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0")
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
|
|
||||||
|
|
||||||
// Third Party Libraries
|
|
||||||
implementation("io.coil-kt:coil:2.2.2")
|
|
||||||
implementation("info.debatty:java-string-similarity:2.0.0")
|
implementation("info.debatty:java-string-similarity:2.0.0")
|
||||||
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun runGitCommand(command: List<String>): String {
|
fun runGitCommand(command: List<String>): String {
|
||||||
|
@ -259,9 +259,7 @@ fun runGitCommand(command: List<String>): String {
|
||||||
ProcessBuilder(command)
|
ProcessBuilder(command)
|
||||||
.directory(project.rootDir)
|
.directory(project.rootDir)
|
||||||
.redirectOutput(ProcessBuilder.Redirect.PIPE)
|
.redirectOutput(ProcessBuilder.Redirect.PIPE)
|
||||||
|
|
||||||
.redirectError(ProcessBuilder.Redirect.PIPE)
|
.redirectError(ProcessBuilder.Redirect.PIPE)
|
||||||
|
|
||||||
.start().inputStream.bufferedReader().use { it.readText() }
|
.start().inputStream.bufferedReader().use { it.readText() }
|
||||||
.trim()
|
.trim()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
7
src/android/app/proguard-rules.pro
vendored
7
src/android/app/proguard-rules.pro
vendored
|
@ -22,3 +22,10 @@
|
||||||
-dontwarn java.beans.Introspector
|
-dontwarn java.beans.Introspector
|
||||||
-dontwarn java.beans.VetoableChangeListener
|
-dontwarn java.beans.VetoableChangeListener
|
||||||
-dontwarn java.beans.VetoableChangeSupport
|
-dontwarn java.beans.VetoableChangeSupport
|
||||||
|
|
||||||
|
# LicenseVerifier protection
|
||||||
|
-keep class org.citron.citron_emu.utils.LicenseVerifier { *; }
|
||||||
|
-keepnames class org.citron.citron_emu.utils.LicenseVerifier
|
||||||
|
-dontskipnonpubliclibraryclasses
|
||||||
|
-dontoptimize
|
||||||
|
-dontpreverify
|
||||||
|
|
|
@ -52,6 +52,7 @@ import org.citron.citron_emu.utils.NativeConfig
|
||||||
import org.citron.citron_emu.utils.NfcReader
|
import org.citron.citron_emu.utils.NfcReader
|
||||||
import org.citron.citron_emu.utils.ParamPackage
|
import org.citron.citron_emu.utils.ParamPackage
|
||||||
import org.citron.citron_emu.utils.ThemeHelper
|
import org.citron.citron_emu.utils.ThemeHelper
|
||||||
|
import org.citron.citron_emu.utils.LicenseVerifier
|
||||||
import java.text.NumberFormat
|
import java.text.NumberFormat
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
@ -79,6 +80,9 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
// Add license verification at the start
|
||||||
|
LicenseVerifier.verifyLicense(this)
|
||||||
|
|
||||||
InputHandler.updateControllerData()
|
InputHandler.updateControllerData()
|
||||||
val players = NativeConfig.getInputSettings(true)
|
val players = NativeConfig.getInputSettings(true)
|
||||||
var hasConfiguredControllers = false
|
var hasConfiguredControllers = false
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
package org.citron.citron_emu.utils
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.AlertDialog
|
||||||
|
import android.content.ClipData
|
||||||
|
import android.content.ClipboardManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.content.pm.Signature
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Process
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
|
object LicenseVerifier {
|
||||||
|
private const val EXPECTED_PACKAGE = "org.citron.citron_emu"
|
||||||
|
private const val OFFICIAL_HASH = "308202e4308201cc020101300d06092a864886f70d010105050030373116301406035504030c0d416e64726f69642044656275673110300e060355040a0c07416e64726f6964310b30090603550406130255533020170d3231303831383138303335305a180f32303531303831313138303335305a30373116301406035504030c0d416e64726f69642044656275673110300e060355040a0c07416e64726f6964310b300906035504061302555330820122300d06092a864886f70d01010105000382010f003082010a0282010100803b4ba8d352ed0475a8442032eadb75ea0a865a0c310c59970bc5f011f162733941a17bac932e060a7f6b00e1d87e640d87951753ee396893769a6e4a60baddc2bf896cd46d5a08c8321879b955eeb6d9f43908029ec6e938433432c5a1ba19da26d8b3dba39f919695626fba5c412b4aba03d85f0246e79af54d6d57347aa6b5095fe916a34262e7060ef4d3f436e7ce03093757fb719b7e72267402289b0fd819673ee44b5aee23237be8e46be08df64b42de09be6090c49d6d0d7d301f0729e25c67eae2d862a87db0aa19db25ba291aae60c7740e0b745af0f1f236dadeb81fe29104a0731eb9091249a94bb56a90239b6496977ebaf1d98b6fa9f679cd0203010001300d06092a864886f70d01010505000382010100784d8e8d28b11bbdb09b5d9e7b8b4fac0d6defd2703d43da63ad4702af76f6ac700f5dcc2f480fbbf6fb664daa64132b36eb7a7880ade5be12919a14c8816b5c1da06870344902680e8ace430705d0a08158d44a3dc710fff6d60b6eb5eff4056bb7d462dafed5b8533c815988805c9f529ef1b70c7c10f1e225eded6db08f847ae805d8b37c174fa0b42cbab1053acb629711e60ce469de383173e714ae2ea76a975169785d1dbe330f803f7f12dd6616703dbaae4d4c327c5174bee83f83635e06f8634cf49d63ba5c3a4f865572740cf9e720e7df1d48fd7a4a2a651d7bb9f40d1cc6b6680b384827a6ea2a44cc1e5168218637fc5da0c3739caca8d21a1d"
|
||||||
|
|
||||||
|
fun verifyLicense(activity: Activity) {
|
||||||
|
val currentPackage = activity.packageName
|
||||||
|
val isDebugBuild = currentPackage.endsWith(".debug")
|
||||||
|
val isEaBuild = currentPackage.endsWith(".ea")
|
||||||
|
|
||||||
|
// Check package name
|
||||||
|
if (!isDebugBuild && !isEaBuild && currentPackage != EXPECTED_PACKAGE) {
|
||||||
|
showViolationDialog(activity)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
activity.packageManager.getPackageInfo(
|
||||||
|
currentPackage,
|
||||||
|
PackageManager.PackageInfoFlags.of(PackageManager.GET_SIGNATURES.toLong())
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
activity.packageManager.getPackageInfo(currentPackage, PackageManager.GET_SIGNATURES)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!verifySignature(packageInfo.signatures)) {
|
||||||
|
showViolationDialog(activity)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
showViolationDialog(activity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun verifySignature(signatures: Array<Signature>?): Boolean {
|
||||||
|
if (signatures == null || signatures.isEmpty()) return false
|
||||||
|
val currentSignature = signatures[0].toCharsString()
|
||||||
|
return currentSignature == OFFICIAL_HASH
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showViolationDialog(activity: Activity) {
|
||||||
|
AlertDialog.Builder(activity)
|
||||||
|
.setTitle("License Violation")
|
||||||
|
.setMessage("This appears to be a modified version of Citron Emulator. " +
|
||||||
|
"Redistributing modified versions without source code violates the GPLv3 License. " +
|
||||||
|
"The application will now close.")
|
||||||
|
.setCancelable(false)
|
||||||
|
.setPositiveButton("Exit") { _, _ ->
|
||||||
|
activity.finish()
|
||||||
|
Process.killProcess(Process.myPid())
|
||||||
|
exitProcess(1)
|
||||||
|
}
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,8 +4,8 @@
|
||||||
|
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application") version "8.8.0" apply false
|
id("com.android.application") version "8.1.2" apply false
|
||||||
id("com.android.library") version "8.8.0" apply false
|
id("com.android.library") version "8.1.2" apply false
|
||||||
id("org.jetbrains.kotlin.android") version "1.9.20" apply false
|
id("org.jetbrains.kotlin.android") version "1.9.20" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue