A comprehensive Android security SDK for detecting root access, tampering, debuggers, emulators, and dynamic analysis tools.
FunGuard SDK is a powerful Android security library designed to protect your applications from various security threats. It provides comprehensive detection capabilities for root access, app tampering, debuggers, emulators, and dynamic analysis tools like Frida.
The SDK runs all security checks asynchronously on background threads to prevent ANR (Application Not Responding) issues, ensuring smooth user experience while maintaining robust security.
- 🔒 Comprehensive Security Checks: Multiple detection mechanisms for various security threats
- 🚀 Non-Blocking: All checks run asynchronously to prevent ANR
- 🎨 Customizable UI: Fully customizable warning dialog with Jetpack Compose
- 📱 Responsive Design: Dialog adapts to different screen sizes
- 🔄 Re-check Capability: Built-in "Check Again" functionality with loading states
- 📧 Support Integration: Optional support email link in dialog
- 📊 Flexible Configuration: Enable/disable individual security checks
- 🎯 Simple API: Single function call with optional parameters
- 📝 Logging Support: Optional debug logging for troubleshooting
Add the FunGuard SDK AAR to your project's build.gradle.kts:
dependencies {
implementation("com.github.Funsol-Projects:FunGuard-SDK:v1.0.2")
}The SDK uses Jetpack Compose for the warning dialog UI. If your project doesn't already use Compose, you need to add the following dependencies:
dependencies {
// Lifecycle for coroutine scope
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
// Coroutines for async operations (required)
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
}Note: If your project already uses Jetpack Compose and Coroutines, you can skip adding these dependencies as they're already included in your project.
Sync your project to download the dependencies.
import com.funsol.securitysdk.FunGuardSDK
import com.funsol.securitysdk.SecurityListener
import com.funsol.securitysdk.models.SecurityResult
import com.funsol.securitysdk.models.SecurityIssue
class MainActivity : ComponentActivity() {
private val securityListener = object : SecurityListener {
override fun onSecurityCheckComplete(result: SecurityResult) {
if (result.isSecure) {
// Device is secure, proceed with app
Log.d("Security", "All checks passed")
} else {
// Security issue detected
// Note: If showWarningDialog = true, SDK handles the dialog automatically
// You don't need to do anything here unless you want custom handling
Log.w("Security", "Issue detected: ${result.issueType}")
}
}
override fun onWarningDialogShown(issueType: SecurityIssue) {
// Called when warning dialog is displayed
// You can log analytics, track events, etc. here
}
override fun onCancel(issueType: SecurityIssue) {
// IMPORTANT: When user clicks Cancel, you MUST close the app yourself
// SDK will NOT automatically close the app
// Save any important data or complete any pending work before closing
finish() // Close the activity/app
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
FunGuardSDK.checkSecurity(
context = this,
showWarningDialog = true,
listener = securityListener
)
}
}For continuous monitoring, it's highly recommended to call checkSecurity() in onResume() instead of onCreate(). This ensures security checks run every time the activity comes to foreground.
override fun onResume() {
super.onResume()
FunGuardSDK.checkSecurity(
context = this,
showWarningDialog = true,
listener = securityListener
)
}Why onResume()?
- Ensures security checks on every app resume
- Detects security issues that may occur while app is in background
- Provides continuous protection throughout app lifecycle
The SDK provides a default dialog that appears when a security issue is detected. However, you can fully customize the dialog appearance and behavior using DialogConfig.
Note: If showWarningDialog = true, the SDK automatically handles the dialog display. You don't need to implement any custom UI in the callback - the SDK manages everything.
import com.funsol.securitysdk.models.DialogConfig
FunGuardSDK.checkSecurity(
context = this,
showWarningDialog = true,
loggingEnabled = true,
dialogConfig = DialogConfig(
title = "Security Alert", // Custom title
description = "A security issue detected.", // Custom description
ctaButtonText = "Retry", // Custom "Check Again" button text
cancelButtonText = "Exit", // Custom "Cancel" button text
showCheckAgainButton = true, // Show/hide "Check Again" button
supportEmail = "support@example.com" // Support email (shown as clickable link)
),
listener = listener
)Partial Customization: You can customize only specific fields - others will use defaults:
DialogConfig(
title = "Custom Title",
supportEmail = "support@example.com"
// Other fields (description, buttons) will use SDK defaults
)FunGuardSDK.checkSecurity(
context = this,
rootCheck = true,
tamperingCheck = true,
fridaCheck = true,
debuggerCheck = false, // Skip debugger check in debug builds
emulatorCheck = false, // Allow emulator for testing
showWarningDialog = true,
listener = listener
)Main function to perform security checks.
fun checkSecurity(
context: Context,
rootCheck: Boolean = true,
tamperingCheck: Boolean = true,
fridaCheck: Boolean = true,
debuggerCheck: Boolean = true,
emulatorCheck: Boolean = true,
showWarningDialog: Boolean = false,
loggingEnabled: Boolean = false,
dialogConfig: DialogConfig? = null,
listener: SecurityListener? = null
): SecurityResultcontext: Application or Activity context (required)rootCheck: Enable/disable root detection (default:true)tamperingCheck: Enable/disable tampering detection (default:true)fridaCheck: Enable/disable Frida detection (default:true)debuggerCheck: Enable/disable debugger detection (default:true)emulatorCheck: Enable/disable emulator detection (default:true)showWarningDialog: Show warning dialog on security failure (default:false)loggingEnabled: Enable debug logging (default:false)dialogConfig: Custom dialog configuration (optional)listener: Callback for security check results (optional)
Returns SecurityResult immediately with isSecure = true (default). The actual result is delivered asynchronously via the SecurityListener.onSecurityCheckComplete() callback.
Interface for receiving security check results and dialog events.
interface SecurityListener {
fun onSecurityCheckComplete(result: SecurityResult)
fun onCancel(issueType: SecurityIssue)
override fun onWarningDialogShown(issueType: SecurityIssue)
}Methods:
onSecurityCheckComplete(result: SecurityResult): Called when security check completesonCancel(issueType: SecurityIssue): Called when user clicks "Cancel" button in dialog
Data class representing the result of a security check.
data class SecurityResult(
val isSecure: Boolean,
val issueType: SecurityIssue? = null
)Properties:
isSecure:trueif device is secure,falseif security issue detectedissueType: Type of security issue (null if secure)
Enum representing different types of security issues.
enum class SecurityIssue {
ROOT_DETECTED,
TAMPERING_DETECTED,
EMULATOR_DETECTED,
FRIDA_DETECTED,
DEBUGGER_DETECTED,
UNKNOWN
}Data class for customizing the warning dialog.
data class DialogConfig(
val title: String? = null,
val description: String? = null,
val ctaButtonText: String? = null,
val cancelButtonText: String? = null,
val showCheckAgainButton: Boolean? = null,
val supportEmail: String? = null
)Properties:
title: Dialog title (uses default if null)description: Dialog description (issue type automatically appended if not present)ctaButtonText: "Check Again" button text (default: "Check Again")cancelButtonText: Cancel button text (default: "Cancel")showCheckAgainButton: Show/hide "Check Again" button (default:true)supportEmail: Support email to display as clickable link (only shown if provided)
Note: All parameters are optional. If a parameter is null, the default value will be used.
class MainActivity : ComponentActivity() {
private val securityListener = object : SecurityListener {
override fun onSecurityCheckComplete(result: SecurityResult) {
if (result.isSecure) {
// Device is secure - proceed normally
// Note: If showWarningDialog = true, SDK handles dialog automatically
// No need to implement custom UI here
} else {
// Security issue detected
// SDK dialog is already shown (if showWarningDialog = true)
// You can add additional logging or analytics here if needed
}
}
override fun onWarningDialogShown(issueType: SecurityIssue) {
// Called when warning dialog is displayed
// You can log analytics, track events, etc. here
}
override fun onCancel(issueType: SecurityIssue) {
// IMPORTANT: User clicked Cancel - close app yourself
// Save any important data before closing
saveImportantData()
// Close the app
finish()
}
}
override fun onResume() {
super.onResume()
// Recommended: Call in onResume for continuous monitoring
FunGuardSDK.checkSecurity(
context = this,
showWarningDialog = true,
listener = securityListener
)
}
private fun saveImportantData() {
// Save user data, preferences, etc.
}
}val isDebug = BuildConfig.DEBUG
FunGuardSDK.checkSecurity(
context = this,
rootCheck = true,
tamperingCheck = true,
fridaCheck = true,
debuggerCheck = !isDebug, // Skip in debug mode
emulatorCheck = !isDebug, // Allow emulator in debug mode
showWarningDialog = true,
loggingEnabled = isDebug, // Enable logging only in debug
listener = securityListener
)FunGuardSDK.checkSecurity(
context = this,
showWarningDialog = true,
dialogConfig = DialogConfig(
title = "Security Alert",
description = "Your device has been flagged for potential security risks.",
ctaButtonText = "Retry Check",
cancelButtonText = "Close App",
showCheckAgainButton = true,
supportEmail = "security@yourapp.com"
),
listener = securityListener
)The SDK performs the following security checks:
Detects if the device is rooted by checking for common root binary paths, root management apps, and su command availability.
Issue Type: SecurityIssue.ROOT_DETECTED
Detects app tampering by checking app signature integrity, system properties, and package manager verification.
Issue Type: SecurityIssue.TAMPERING_DETECTED
Detects active debuggers by checking Debug.isDebuggerConnected(), TracerPid, JDWP status, and debug flags.
Issue Type: SecurityIssue.DEBUGGER_DETECTED
Detects Frida dynamic analysis framework by checking Frida server ports, processes, and libraries.
Issue Type: SecurityIssue.FRIDA_DETECTED
Detects if running on an emulator by checking QEMU-related files, build properties, and emulator-specific characteristics.
Issue Type: SecurityIssue.EMULATOR_DETECTED
When showWarningDialog = true:
- SDK automatically displays the dialog when security issue is detected
- You don't need to implement any custom UI in
onSecurityCheckComplete()callback - SDK handles all dialog interactions (Check Again, Cancel) automatically
When showWarningDialog = false:
- You must handle security failures yourself in
onSecurityCheckComplete()callback - Implement your own custom UI or logic for security issues
onCancel() callback is triggered. You MUST close the app yourself - the SDK will NOT automatically close the app.
override fun onCancel(issueType: SecurityIssue) {
// CRITICAL: Save any important data or complete pending work
saveUserData()
completePendingOperations()
// Then close the app
finish() // For Activity
// or System.exit(0) // For complete app termination
}Why manual close?
- SDK doesn't know your app's data persistence requirements
- You may need to save user data, clear caches, or complete transactions
- Gives you full control over app termination process
val isDebug = BuildConfig.DEBUG
FunGuardSDK.checkSecurity(
context = this,
debuggerCheck = !isDebug, // Skip in debug builds
emulatorCheck = !isDebug, // Allow emulator for testing
loggingEnabled = isDebug, // Enable logging only in debug
showWarningDialog = true,
listener = listener
)Always provide a support email in production builds:
dialogConfig = DialogConfig(
supportEmail = "security-support@yourapp.com"
)loggingEnabled = !BuildConfig.DEBUG- Ensure
showWarningDialog = true - Verify that
listeneris notnull - Check that you're using an
Activitycontext - Ensure the activity is not finishing or destroyed
- Review which checks are enabled
- Consider disabling
emulatorCheckif you need to support emulators - Disable
debuggerCheckin debug builds - Use
loggingEnabled = trueto debug what's being detected
Copyright (c) 2025 Funsol Technologies Pvt Ltd
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
