Created
February 2, 2026 16:00
-
-
Save Oleg-Beloy/93bf066995e3da0a1375156dbab7c394 to your computer and use it in GitHub Desktop.
Documentation Android AV SDK 2.2.1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # PointWild Antivirus Android SDK | |
| [](https://github.com/AnchorFree/android-av-sdk/actions/workflows/ci.yml) | |
| # SDK Quick Start Guide | |
| This guide explains how to integrate and use the SDK in an Android project. | |
| ## 0. API | |
| * [Modern SDK](ScannerSDK/src/main/java/com/pointwild/avsdk/contract/AntivirusSdk.kt) and recommended api with | |
| coroutines support, a preferable way to use in a kotlin app | |
| * [Compat api](ScannerSDK/src/main/java/com/pointwild/avsdk/contract/AntivirusSdkCompat.kt) wrapper for java | |
| and kotlin apps, which do not support coroutines | |
| * See [scan modes](ScannerSDK/src/main/java/com/pointwild/avsdk/contract/ScanMode.kt) for details | |
| about scan modes | |
| ## 1. Installation | |
| Add the SDK to your `build.gradle`: | |
| ```gradle | |
| dependencies { | |
| implementation("com.pointwild:avsdk:{RELEASE_VERSION}") | |
| } | |
| ``` | |
| ### Aura Jfrog remote repository | |
| For those, who have private repository credentials, add the following to the end of `repositories` | |
| section: | |
| ```gradle | |
| pluginManagement { | |
| repositories { | |
| ... | |
| maven { | |
| url = uri("https://aura.jfrog.io/artifactory/MaxSecureAndroid") | |
| credentials { | |
| username = "username" | |
| password = "XXXXXXXX" | |
| } | |
| } | |
| } | |
| } | |
| ``` | |
| ### Local maven repository | |
| * Get avsdk-x.x.x.zip | |
| * Unpack to project root. The resulting folders should have `build/avsdk/com/pointwild/..` structure | |
| * add the following to the end of `repositories` section: | |
| Add | |
| ```gradle | |
| pluginManagement { | |
| repositories { | |
| ... | |
| maven { | |
| url = uri("${rootDir}/build/avsdk") | |
| } | |
| } | |
| } | |
| ``` | |
| ## 1.1 Dependency Requirements | |
| The SDK has the following dependency constraints: | |
| - **Min SDK**: 23 (Android 6.0) | |
| - **OkHttp**: 4.12.0 | |
| - **AGP (Android Gradle Plugin)**: 8.13.2 | |
| - **Kotlin**: 2.2.21 | |
| Ensure your project meets these requirements for compatibility. | |
| If you need changes here, let us know. | |
| ## 2. Initialization and authentication | |
| ### 2.1 Company license | |
| Pass company license as param to the init api: | |
| ```kotlin | |
| scanner.initialiseSdk("YOUR_COMPANY_LICENSE") | |
| ``` | |
| To get the company license, share with us **SHA-256** of your app's signing certificate. You can get it, using the following command: | |
| ```keytool -exportcert -keystore <yourkeystore.jks> -alias <mykey> -storepass <storepass> | openssl sha256 -binary | openssl base64``` | |
| it should be your signing certificate of an app. | |
| ``` groovy | |
| debug { | |
| signingConfig signingConfigs.debug | |
| } | |
| release { | |
| signingConfig signingConfigs.release | |
| } | |
| ``` | |
| So, if you have different certificates for debug and prod versions of an app, you’ll need two keys to be generated and used correspondingly | |
| ### 2.2 Pango auth | |
| To enable user authentication, configure Pango Auth first. | |
| Create a `PangoAuthConfiguration` with your credentials and pass it to the SDK: | |
| ```kotlin | |
| val authConfig = PangoAuthConfiguration( | |
| apiKey = "YOUR_API_KEY", | |
| productName = "YOUR_PRODUCT_NAME" | |
| ) | |
| scanner.initialiseSdk(authConfig) | |
| ``` | |
| After successful initialization, the SDK is ready to authenticate users. | |
| Use the signIn method to authenticate a user with their credentials: | |
| ```kotlin | |
| scanner.signIn( | |
| username = "username", | |
| password = "password" | |
| ) | |
| ``` | |
| To sign out the currently authenticated user, call: | |
| ```kotlin | |
| scanner.signOut() | |
| ``` | |
| ## 3. Permissions | |
| Handle permissions and add them to your AndroidManifest.xml: | |
| ```xml | |
| ... | |
| <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" | |
| android:maxSdkVersion="32" /><uses-permission | |
| android:name="android.permission.WRITE_EXTERNAL_STORAGE" | |
| android:maxSdkVersion="32" /><uses-permission | |
| android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" /> | |
| <application android:requestLegacyExternalStorage="true" | |
| android:requestRawExternalStorageAccess="true"... | |
| ``` | |
| ### Using AntivirusPermissionManager | |
| The SDK provides `AntivirusPermissionManager` to handle storage permissions automatically. This is | |
| the recommended approach for managing permissions in your app. | |
| #### Key Features | |
| - **Automatic Lifecycle Management**: Automatically releases resources when activity/fragment is | |
| destroyed | |
| - **Google Play Compliance**: Handles disclosure dialog requirements for `MANAGE_EXTERNAL_STORAGE` | |
| permission | |
| - **Cross-Platform Support**: Works with both legacy storage permissions (API < 30) and scoped | |
| storage (API ≥ 30) | |
| - **Request Code Support**: Allows different actions to be triggered based on permission request | |
| source | |
| #### Basic Setup | |
| ```kotlin | |
| import com.pointwild.avsdk.permission.AntivirusPermissionManager | |
| import com.pointwild.avsdk.permission.PermissionCallback | |
| import com.pointwild.avsdk.permission.PermissionRequest | |
| class YourActivity : ComponentActivity(), PermissionCallback { | |
| private val permissionManager = AntivirusPermissionManager.create() | |
| override fun onCreate(savedInstanceState: Bundle?) { | |
| super.onCreate(savedInstanceState) | |
| // Attach permission manager to activity | |
| permissionManager.attachTo(this, this) | |
| } | |
| override fun onDestroy() { | |
| super.onDestroy() | |
| permissionManager.release() | |
| } | |
| // Implement PermissionCallback | |
| override fun onPermissionResult(requestCode: Int, isGranted: Boolean) { | |
| if (isGranted) { | |
| // Permissions granted, proceed with scanning | |
| when (requestCode) { | |
| R.id.btnScanAll -> startScanAll() | |
| R.id.btnScanFiles -> startScanFiles() | |
| // Handle other scan types | |
| } | |
| } else { | |
| // Show error message to user | |
| Toast.makeText(this, "Storage permissions required for scanning", Toast.LENGTH_SHORT) | |
| .show() | |
| } | |
| } | |
| override fun onStoragePermissionDisclosureDialogRequested(request: PermissionRequest) { | |
| // Show disclosure dialog explaining why permissions are needed | |
| MaterialAlertDialogBuilder(this) | |
| .setTitle("Storage Access Required") | |
| .setMessage("This app needs access to storage to scan files for viruses and malware.") | |
| .setPositiveButton("Continue") { _, _ -> | |
| request.proceed() // Proceed with permission request | |
| } | |
| .setNegativeButton("Cancel") { _, _ -> | |
| // Handle user cancellation | |
| } | |
| .show() | |
| } | |
| private fun requestScanPermissions(requestCode: Int) { | |
| permissionManager.requestStoragePermission(requestCode, false) | |
| } | |
| } | |
| ``` | |
| #### Using in Fragment | |
| ```kotlin | |
| class YourFragment : Fragment(), PermissionCallback { | |
| private val permissionManager = AntivirusPermissionManager.create() | |
| override fun onCreate(savedInstanceState: Bundle?) { | |
| super.onCreate(savedInstanceState) | |
| // Attach permission manager to fragment | |
| permissionManager.attachTo(this, this) | |
| } | |
| override fun onDestroy() { | |
| super.onDestroy() | |
| permissionManager.release() | |
| } | |
| // Implement PermissionCallback interface | |
| override fun onPermissionResult(requestCode: Int, isGranted: Boolean) { | |
| if (isGranted) { | |
| // Handle successful permission grant | |
| when (requestCode) { | |
| R.id.btnScanAll -> viewModel.startScanAll() | |
| R.id.btnScanFiles -> viewModel.startScanFiles() | |
| } | |
| } | |
| } | |
| override fun onStoragePermissionDisclosureDialogRequested(request: PermissionRequest) { | |
| // Show disclosure dialog | |
| MaterialAlertDialogBuilder(requireContext()) | |
| .setTitle("Storage Access Required") | |
| .setMessage("This app needs access to storage to scan files for viruses and malware.") | |
| .setPositiveButton("Continue") { _, _ -> | |
| request.proceed() | |
| } | |
| .setNegativeButton("Cancel", null) | |
| .show() | |
| } | |
| } | |
| ``` | |
| #### Important Notes | |
| 1. **Google Play Compliance**: For apps targeting API 30+, you must show a disclosure dialog before | |
| requesting `MANAGE_EXTERNAL_STORAGE` permission. The `AntivirusPermissionManager` handles this | |
| automatically through the `onStoragePermissionDisclosureDialogRequested` callback. | |
| 2. **Disclosure Dialog**: You must implement the disclosure dialog in | |
| `onStoragePermissionDisclosureDialogRequested` and call `request.proceed()` when the user | |
| confirms. Record a video of this dialog and scan start for Google Play submission. | |
| 3. **Permission Check**: Use `areStoragePermissionsGranted()` to check if permissions are already | |
| granted before requesting them. | |
| ```kotlin | |
| if (permissionManager.areStoragePermissionsGranted()) { | |
| // Permissions already granted, proceed with scanning | |
| startScanning() | |
| } else { | |
| // Request permissions first | |
| permissionManager.requestStoragePermission(R.id.btnScan, false) | |
| } | |
| ``` | |
| ## 4. Scan files | |
| Use this api(s) for scanning single or multiple files: | |
| ```kotlin | |
| val files = listOf<File>() | |
| val result = scanner.scanFile(files.first()) | |
| // other possible usages | |
| scanner.scanFiles(files) | |
| scanner.scanFiles(File("/storage/emulated/0/Download")) | |
| ``` | |
| ## 5. Scan apps | |
| Use this api(s) for scanning single or multiple apps: | |
| ```kotlin | |
| val result = scanner.scanInstalledApps { progress -> | |
| _scanProgress.value = progress | |
| } | |
| // other possible usages | |
| val apps: List<ApplicationInfo> = listOf(...) | |
| scanner.scanApps(apps) | |
| scanner.scanApp(apps.first()) | |
| ``` | |
| ### Using AppUninstaller | |
| The SDK provides optional `AppUninstaller` to handle and simplify uninstallation of malicious apps. | |
| It uses the system | |
| uninstall dialog with user consent. | |
| When it is setup, a client can call `appUninstaller.uninstall(maliciousApp)` or | |
| `appUninstaller.uninstall(packageName)` | |
| #### Key Features | |
| - **User Consent**: Opens system uninstall dialog requiring user confirmation | |
| - **Result Callbacks**: Detects successful uninstall, user cancellation, and failures | |
| - **No Special Permissions**: Works without `REQUEST_DELETE_PACKAGES` permission | |
| - **Automatic Lifecycle Management**: Automatically releases resources when activity/fragment is | |
| destroyed | |
| #### Basic Setup (Can be also in a fragment) | |
| ```kotlin | |
| import com.pointwild.avsdk.uninstaller.AppUninstaller | |
| import com.pointwild.avsdk.uninstaller.UninstallCallback | |
| class YourActivity : ComponentActivity(), UninstallCallback { | |
| private val appUninstaller = AppUninstaller.create() | |
| override fun onCreate(savedInstanceState: Bundle?) { | |
| super.onCreate(savedInstanceState) | |
| // Attach uninstaller to activity | |
| appUninstaller.attachTo(this, this) | |
| } | |
| override fun onDestroy() { | |
| super.onDestroy() | |
| appUninstaller.release() | |
| } | |
| // Implement UninstallCallback | |
| override fun onSuccess(packageName: String) { | |
| Toast.makeText(this, "App uninstalled: $packageName", Toast.LENGTH_SHORT).show() | |
| } | |
| override fun onUserCanceled(packageName: String) { | |
| Toast.makeText(this, "Uninstall cancelled: $packageName", Toast.LENGTH_SHORT).show() | |
| } | |
| override fun onFailure(packageName: String, status: Int, message: String?) { | |
| Toast.makeText(this, "Uninstall failed: $message", Toast.LENGTH_SHORT).show() | |
| } | |
| private fun uninstallApp(maliciousApp: DetectedThreat.MaliciousApp) { | |
| appUninstaller.uninstall(maliciousApp) | |
| } | |
| private fun uninstallByPackageName(packageName: String) { | |
| appUninstaller.uninstall(packageName) | |
| } | |
| } | |
| ``` | |
| #### Important Notes | |
| 1. **Attachment Required**: Call `attachTo()` in `onCreate()` before `super.onCreate()` to register | |
| the activity result launcher before the lifecycle reaches STARTED state. | |
| 2. **User Consent**: The uninstall dialog is handled by the Android system. The user must confirm | |
| the uninstallation. | |
| 3. **Google Play Compliance**: This feature requires having | |
| `android.permission.REQUEST_DELETE_PACKAGES`. | |
| It must be then disclosed on google play | |
| ## 6. Scan all | |
| ```kotlin | |
| scanner.scanAll { progress -> | |
| _scanProgress.value = progress | |
| } | |
| ``` | |
| ## 7. Scan Progress Tracking | |
| The SDK provides real-time progress updates through the `ScanProgress` data class, which offers | |
| comprehensive information about the current scanning state. This enables you to build rich user | |
| interfaces that show exactly what's being scanned and the overall progress. | |
| ### Key Features | |
| - **Real-time Entry Tracking**: See which entries are currently being scanned, not just completed | |
| ones | |
| - **Progress Percentage**: Accurate percentage calculation based on total entries including online | |
| scan overhead | |
| - **Threat Detection Count**: Track how many infected entries have been detected on a go | |
| ### ScanProgress Properties | |
| ```kotlin | |
| data class ScanProgress( | |
| val scannedEntry: ScanEntry? = null, // Last completed entry | |
| val scanningEntries: Set<ScanEntry> = emptySet(), // Currently scanning entries | |
| val scanCount: Int, // Number of entries scanned so far | |
| val infectedEntryCount: Int, // Number of infected entries found | |
| val totalScanSize: Int, // Total number of entries to scan | |
| val percent: Int // Progress percentage (0-100) | |
| ) | |
| ``` | |
| ### Understanding ScanEntry | |
| `ScanEntry` represents an entry being scanned and can be either: | |
| - **`ScanEntry.AppEntry`**: An installed application being scanned | |
| - **`ScanEntry.FileEntry`**: A file being scanned | |
| Both types provide a `title` property for display purposes: | |
| - `AppEntry.title`: Returns the package name | |
| - `FileEntry.title`: Returns the file name | |
| ### Progress Update Behavior | |
| The SDK emits progress updates in 3 scenarios: | |
| 1. **When Starting to Scan an Entry**: Progress is emitted immediately when scanning begins, showing | |
| the entry in `scanningEntries`. This is especially useful for large entries (like multidex apps) | |
| that take time to scan, as users see activity right away. | |
| 2. **When Finishing an Entry**: Progress is emitted when an entry completes, moving it from | |
| `scanningEntries` to `scannedEntry`. | |
| 3. **When Online Scan is Done**: Progress is emitted when a chunk of entries is scanned online | |
| ### Best Practices | |
| 1. **Display Scanning Entries**: Show `scanningEntries` to users so they know what's currently being | |
| processed, especially for long-running scans | |
| 2. **Update UI Frequently**: Progress callbacks are called frequently; debounce UI updates if needed | |
| for performance | |
| 3. **Show Both States**: Display both completed (`scannedEntry`) and in-progress (`scanningEntries`) | |
| entries for best user experience | |
| ## 8. Signatures update | |
| Signatures database is downloaded on a first scan or if `requestUpdateSignatures` is called. | |
| It is used to compare signatures of apps & files under scan with known malicious signatures | |
| database. | |
| For reaching maximum protection reasons a scan makes an incremental signatures update, if new | |
| signatures are available. | |
| If server signatures version exceeds local version on **30** versions or more, then signatures | |
| database | |
| is redownloaded. | |
| For performance reasons in the same logic we introduced a threshold that signatures update could be | |
| done only once a day. | |
| On the other hand, `requestUpdateSignatures` is needed in case a client app wants to preload | |
| signatures, silently on an app launch, for example. To speed up a next scan, which will wait for an | |
| ongoing update. | |
| If `requestUpdateSignatures` is called, SDK compares `getVdfLocalVersion` and `getVdfServerVersion` | |
| on its own, and an incremental update will be made only if new signatures are available | |
| ### SignatureUpdateListener | |
| The SDK provides `SignatureUpdateListener` to monitor virus signature database updates and | |
| initialization states. This is essential for tracking the status of signature downloads and ensuring | |
| your app can provide proper user feedback during updates. | |
| SDK synchronise with signatures automatically when a scan is requested. | |
| #### Key Features | |
| - **Real-time Progress Tracking**: Monitor download progress with precise percentage updates | |
| - **Error Handling**: Get detailed error information when signature updates fail | |
| - **Version Information**: Access current signature database version details | |
| - **State Management**: Track the complete lifecycle of signature initialization and updates | |
| #### Basic Setup | |
| ```kotlin | |
| import com.pointwild.avsdk.contract.SignatureUpdateListener | |
| import com.pointwild.avsdk.contract.SignaturesInitialisationState | |
| import com.pointwild.avsdk.contract.SignaturesVersion | |
| class YourActivity : ComponentActivity() { | |
| private lateinit var scanner: AntivirusSdkCompat | |
| override fun onCreate(savedInstanceState: Bundle?) { | |
| super.onCreate(savedInstanceState) | |
| scanner = AntivirusSdkCompat.build() | |
| // Set up signature update listener | |
| setupSignatureUpdateListener() | |
| } | |
| private fun setupSignatureUpdateListener() { | |
| scanner.setSignaturesUpdateListener(object : SignatureUpdateListener { | |
| override fun onSignatureUpdateStateChange(state: SignaturesInitialisationState) { | |
| when (state) { | |
| is SignaturesInitialisationState.NotInitialized -> { | |
| // No signatures database exists yet | |
| showMessage("No virus signatures found") | |
| } | |
| is SignaturesInitialisationState.Initialised -> { | |
| // Signatures database exists but may need updating | |
| showMessage("Signatures database ready") | |
| } | |
| is SignaturesInitialisationState.InProgress -> { | |
| // Signature update in progress | |
| val progress = (state.progress * 100).toInt() | |
| showMessage("Downloading virus signatures: $progress%") | |
| updateProgressBar(state.progress) | |
| } | |
| SignaturesInitialisationState.Paused -> { | |
| // Update is paused waiting for network connection | |
| showMessage("Waiting for network connection...") | |
| } | |
| is SignaturesInitialisationState.Ready -> { | |
| // Signatures are up to date and ready | |
| val version = state.vdfVersion | |
| showMessage("Signatures updated: ${version.versionName}") | |
| enableScanning() | |
| } | |
| is SignaturesInitialisationState.Error -> { | |
| // Error occurred during signature update | |
| showError("Failed to update signatures: ${state.error.message}") | |
| } | |
| } | |
| } | |
| }) | |
| } | |
| private fun requestSignatureUpdate() { | |
| scanner.requestUpdateSignatures() | |
| } | |
| } | |
| ``` | |
| #### Signature States Explained | |
| The `SignaturesInitialisationState` provides detailed information about the current state of virus | |
| signatures: | |
| - **`NotInitialized`**: No signatures database exists on the device. First-time setup required. | |
| - **`Initialised`**: Signatures database exists but may be outdated. Ready for scanning with | |
| existing signatures. | |
| - **`InProgress(progress: Double)`**: Signature update is currently downloading. Progress ranges | |
| from 0.0 to 1.0. | |
| - **`Paused`**: Signature update is paused waiting for internet connection to be restored. The update | |
| will automatically resume when network connectivity is reestablished. | |
| - **`Ready(vdfVersion: SignaturesVersion)`**: Signatures are up to date and ready for scanning. | |
| Contains version information. | |
| - **`Error(error: Throwable)`**: An error occurred during signature update. Contains the error | |
| details. | |
| #### Signature Version Information | |
| ```kotlin | |
| // Access signature version information | |
| when (val state = signatureUpdateStatus.value) { | |
| is SignaturesInitialisationState.Ready -> { | |
| val version = state.vdfVersion | |
| val versionId = version.id // Incremental version ID | |
| val versionName = version.versionName // Human-readable version string | |
| println("Signature version: $versionName (ID: $versionId)") | |
| } | |
| // Handle other states... | |
| } | |
| ``` | |
| ## 9. [Optional] SDK configuration | |
| ```kotlin | |
| scanner = AntivirusSdkCompat.build() | |
| // configure sdk | |
| scanner.configureSdk( | |
| ScannerConfiguration( | |
| shouldScanSystemApps = true, | |
| skipStrategy = SkipStrategy.NONE, | |
| enableZipScanning = true, | |
| logLevel = LogLevel.VERBOSE, | |
| scanApkSizeLimit = 3 * 1024 * 1024, | |
| zipScanSizeLimit = 50 * 1024 * 1024 // 50MB limit | |
| ) | |
| ) | |
| ``` | |
| Or you can use our default settings | |
| ## 10. [Optional] Realtime Scan Service | |
| The SDK provides a realtime scanning service that monitors app installations and file system | |
| changes. This feature requires a **foreground service** with a persistent notification (Android | |
| system requirement). | |
| ### Scanning Modes | |
| - **App Install Monitoring**: Scans newly installed apps in real-time, event-based approach | |
| - **File System Monitoring**: Global observer that recursively scans all changed files every 4 hours | |
| The scanning mode is configurable via `RealtimeScanConfig`. | |
| ### Enabling Realtime Scan | |
| Use `RealtimeScanSdk` to activate and configure the feature: | |
| ```kotlin | |
| import com.pointwild.avsdk.contract.RealtimeScanSdk | |
| import com.pointwild.avsdk.contract.RealtimeScanConfig | |
| import com.pointwild.avsdk.contract.RealtimeScanMode | |
| // Get SDK instance (implements RealtimeScanSdk) | |
| val scanner = AntivirusSdkCompat.build() | |
| // Optional: Configure notification and scan mode before enabling | |
| scanner.configureRealtimeScan( | |
| RealtimeScanConfig( | |
| scanMode = RealtimeScanMode.APPS_ONLY, // or RealtimeScanMode.ALL for apps + files | |
| notificationTitleRes = R.string.your_notification_title, | |
| notificationTextRes = R.string.your_notification_text, | |
| smallIconRes = R.drawable.your_notification_icon, | |
| notificationClickAction = "com.example.app.ACTION_OPEN_SCAN_STATUS" // optional | |
| ) | |
| ) | |
| // Enable realtime scanning | |
| scanner.enableRealtimeScan(true) | |
| // Check if enabled | |
| val isEnabled = scanner.isRealtimeScanEnabled() | |
| // Disable realtime scanning | |
| scanner.enableRealtimeScan(false) | |
| ``` | |
| ### Receiving Scan Results | |
| Create a receiver extending `RealtimeScanResultBroadcastReceiver`: | |
| ```kotlin | |
| class MyRealtimeScanReceiver : RealtimeScanResultBroadcastReceiver() { | |
| override fun onRealtimeScanResult( | |
| context: Context, | |
| entry: ScanEntry, | |
| result: ScanResult | |
| ) { | |
| when (entry) { | |
| is ScanEntry.AppRealtimeEntry -> { | |
| // Handle app scan result | |
| val packageName = entry.applicationInfo.packageName | |
| } | |
| is ScanEntry.FileRealtimeEntry -> { | |
| // Handle file scan result | |
| val filePath = entry.path | |
| } | |
| } | |
| when (result) { | |
| is ScanResult.Success -> { | |
| if (result.detectedThreats.isNotEmpty()) { | |
| // Threat detected! | |
| } | |
| } | |
| is ScanResult.Error -> { | |
| // Handle error | |
| } | |
| } | |
| } | |
| } | |
| ``` | |
| Register it in your `AndroidManifest.xml`: | |
| ```xml | |
| <receiver android:name=".MyRealtimeScanReceiver" android:enabled="true" android:exported="false"> | |
| <intent-filter> | |
| <action android:name="com.pointwild.avsdk.REALTIME_SCAN_RESULT" /> | |
| </intent-filter> | |
| </receiver> | |
| ``` | |
| ### Manifest Configuration | |
| Since the realtime scan components are not included in the SDK manifest by default, you must add the | |
| following entries to your app's `AndroidManifest.xml`: | |
| ```xml | |
| <!-- Boot receiver to restart realtime scanning service after system reboot --> | |
| <receiver | |
| android:name="com.pointwild.avsdk.realtime.RealtimeScanBootReceiver" | |
| android:enabled="true" | |
| android:exported="true"> | |
| <intent-filter android:priority="1000"> | |
| <action android:name="android.intent.action.BOOT_COMPLETED" /> | |
| </intent-filter> | |
| <intent-filter> | |
| <action android:name="android.intent.action.MY_PACKAGE_REPLACED" /> | |
| </intent-filter> | |
| </receiver> | |
| <!-- Foreground service for realtime app/file scanning --> | |
| <service | |
| android:name="com.pointwild.avsdk.realtime.RealtimeScanService" | |
| android:enabled="true" | |
| android:exported="false" | |
| android:foregroundServiceType="specialUse"> | |
| <property | |
| android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" | |
| android:value="realtime-device-security-app-install-filesystem-change-monitor" /> | |
| </service> | |
| ``` | |
| ### Important Notes | |
| 1. **Foreground Service Requirement**: The realtime scanning logic must have the foreground service | |
| running and display a notification to the user — this is an Android system requirement. Covered | |
| by SDK | |
| 2. **Boot Receiver**: Ensures the realtime scanning service automatically restarts after device | |
| reboot or app update. | |
| 3. **Performance**: Deep performance testing for file system monitoring has not been conducted. For | |
| lighter resource usage, consider using app install scanning mode only. | |
| ## 11. Demo app | |
| See [demo app](demoApp) where features of the SDK are showcased. You can build it on your own or use | |
| a compiled sample which comes with a github release. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment