Spaces:
Running
Running
Commit ·
c25dcd7
0
Parent(s):
Deploy source code to Hugging Face without binaries
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitignore +53 -0
- .metadata +45 -0
- README.md +60 -0
- analysis_options.yaml +28 -0
- analyze_current.txt +39 -0
- analyze_final.txt +29 -0
- analyze_out.txt +63 -0
- analyze_verify.txt +25 -0
- android/.gitignore +14 -0
- android/app/build.gradle.kts +53 -0
- android/app/src/debug/AndroidManifest.xml +7 -0
- android/app/src/main/AndroidManifest.xml +108 -0
- android/app/src/main/kotlin/in/inmodel/premithus/DomainBlocker.kt +159 -0
- android/app/src/main/kotlin/in/inmodel/premithus/KavachaVpnService.kt +335 -0
- android/app/src/main/kotlin/in/inmodel/premithus/KeyboardIMEService.kt +77 -0
- android/app/src/main/kotlin/in/inmodel/premithus/LockedAppDao.kt +43 -0
- android/app/src/main/kotlin/in/inmodel/premithus/MainActivity.kt +273 -0
- android/app/src/main/kotlin/in/inmodel/premithus/OverlayService.kt +515 -0
- android/app/src/main/kotlin/in/inmodel/premithus/PremithiusAccessibilityService.kt +135 -0
- android/app/src/main/kotlin/in/inmodel/premithus/PremithiusDatabase.kt +99 -0
- android/app/src/main/kotlin/in/inmodel/premithus/PremithiusWidget.kt +97 -0
- android/app/src/main/kotlin/in/inmodel/premithus/SmsReceiver.kt +26 -0
- android/app/src/main/kotlin/in/inmodel/premithus/TelemetryHelper.kt +251 -0
- android/app/src/main/res/drawable-v21/launch_background.xml +12 -0
- android/app/src/main/res/drawable/launch_background.xml +12 -0
- android/app/src/main/res/layout/widget_4x2.xml +85 -0
- android/app/src/main/res/layout/widget_layout.xml +52 -0
- android/app/src/main/res/values-night/styles.xml +18 -0
- android/app/src/main/res/values/colors.xml +7 -0
- android/app/src/main/res/values/strings.xml +8 -0
- android/app/src/main/res/values/styles.xml +18 -0
- android/app/src/main/res/xml/accessibility_service_config.xml +11 -0
- android/app/src/main/res/xml/method.xml +7 -0
- android/app/src/main/res/xml/premithius_widget_info.xml +12 -0
- android/app/src/profile/AndroidManifest.xml +7 -0
- android/build.gradle.kts +25 -0
- android/build/reports/problems/problems-report.html +0 -0
- android/gradle.properties +2 -0
- android/gradle/wrapper/gradle-wrapper.properties +5 -0
- android/settings.gradle.kts +26 -0
- assets/blocklist/india_scam_domains.txt +68 -0
- assets/seed_embeddings.json +0 -0
- build_err.txt +296 -0
- convert_bert.py +42 -0
- demo_script_v0.0.2.md +28 -0
- hf_space +1 -0
- index.html +19 -0
- ios/.gitignore +34 -0
- ios/Flutter/AppFrameworkInfo.plist +24 -0
- ios/Flutter/Debug.xcconfig +2 -0
.gitignore
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Miscellaneous
|
| 2 |
+
*.class
|
| 3 |
+
*.log
|
| 4 |
+
*.pyc
|
| 5 |
+
*.swp
|
| 6 |
+
.DS_Store
|
| 7 |
+
.atom/
|
| 8 |
+
.build/
|
| 9 |
+
.buildlog/
|
| 10 |
+
.history
|
| 11 |
+
.svn/
|
| 12 |
+
.swiftpm/
|
| 13 |
+
migrate_working_dir/
|
| 14 |
+
|
| 15 |
+
# IntelliJ related
|
| 16 |
+
*.iml
|
| 17 |
+
*.ipr
|
| 18 |
+
*.iws
|
| 19 |
+
.idea/
|
| 20 |
+
|
| 21 |
+
# The .vscode folder contains launch configuration and tasks you configure in
|
| 22 |
+
# VS Code which you may wish to be included in version control, so this line
|
| 23 |
+
# is commented out by default.
|
| 24 |
+
#.vscode/
|
| 25 |
+
|
| 26 |
+
# Flutter/Dart/Pub related
|
| 27 |
+
**/doc/api/
|
| 28 |
+
**/ios/Flutter/.last_build_id
|
| 29 |
+
.dart_tool/
|
| 30 |
+
.flutter-plugins-dependencies
|
| 31 |
+
.pub-cache/
|
| 32 |
+
.pub/
|
| 33 |
+
/build/
|
| 34 |
+
/coverage/
|
| 35 |
+
|
| 36 |
+
# Symbolication related
|
| 37 |
+
app.*.symbols
|
| 38 |
+
|
| 39 |
+
# Obfuscation related
|
| 40 |
+
app.*.map.json
|
| 41 |
+
|
| 42 |
+
# Android Studio will place build artifacts here
|
| 43 |
+
/android/app/debug
|
| 44 |
+
/android/app/profile
|
| 45 |
+
/android/app/release
|
| 46 |
+
rf_env/
|
| 47 |
+
releases/
|
| 48 |
+
*.so
|
| 49 |
+
*.png
|
| 50 |
+
*.tflite
|
| 51 |
+
*.a
|
| 52 |
+
*.zip
|
| 53 |
+
*.jar
|
.metadata
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file tracks properties of this Flutter project.
|
| 2 |
+
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
| 3 |
+
#
|
| 4 |
+
# This file should be version controlled and should not be manually edited.
|
| 5 |
+
|
| 6 |
+
version:
|
| 7 |
+
revision: "ff37bef603469fb030f2b72995ab929ccfc227f0"
|
| 8 |
+
channel: "stable"
|
| 9 |
+
|
| 10 |
+
project_type: app
|
| 11 |
+
|
| 12 |
+
# Tracks metadata for the flutter migrate command
|
| 13 |
+
migration:
|
| 14 |
+
platforms:
|
| 15 |
+
- platform: root
|
| 16 |
+
create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
|
| 17 |
+
base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
|
| 18 |
+
- platform: android
|
| 19 |
+
create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
|
| 20 |
+
base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
|
| 21 |
+
- platform: ios
|
| 22 |
+
create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
|
| 23 |
+
base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
|
| 24 |
+
- platform: linux
|
| 25 |
+
create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
|
| 26 |
+
base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
|
| 27 |
+
- platform: macos
|
| 28 |
+
create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
|
| 29 |
+
base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
|
| 30 |
+
- platform: web
|
| 31 |
+
create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
|
| 32 |
+
base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
|
| 33 |
+
- platform: windows
|
| 34 |
+
create_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
|
| 35 |
+
base_revision: ff37bef603469fb030f2b72995ab929ccfc227f0
|
| 36 |
+
|
| 37 |
+
# User provided section
|
| 38 |
+
|
| 39 |
+
# List of Local paths (relative to this file) that should be
|
| 40 |
+
# ignored by the migrate tool.
|
| 41 |
+
#
|
| 42 |
+
# Files that are not part of the templates will be ignored by default.
|
| 43 |
+
unmanaged_files:
|
| 44 |
+
- 'lib/main.dart'
|
| 45 |
+
- 'ios/Runner.xcodeproj/project.pbxproj'
|
README.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: Prem
|
| 3 |
+
emoji: 🔥
|
| 4 |
+
colorFrom: red
|
| 5 |
+
colorTo: yellow
|
| 6 |
+
sdk: static
|
| 7 |
+
pinned: false
|
| 8 |
+
short_description: In device co-pilot
|
| 9 |
+
---
|
| 10 |
+
# Premithius V0.0.3
|
| 11 |
+
**India-First On-Device AI Security | Zero LLM Footprint | ARMadillo Architecture**
|
| 12 |
+
|
| 13 |
+
Premithius is an uncompromising, edge-native security layer for Android devices. Designed specifically for the Indian cybersecurity context, it proactively intercepts sophisticated threats like ₹1 collect requests, Aadhaar/PAN data leaks, and zero-day scams before they ever reach the user's eyes—all without sending a single byte of personal data to the cloud.
|
| 14 |
+
|
| 15 |
+
---
|
| 16 |
+
|
| 17 |
+
## 🛡️ Kavacha Engine (5-Layer Defense)
|
| 18 |
+
Inspired by NVIDIA's ARMadillo architecture, Premithius utilizes a DOCA FLOW-style early-exit pipeline. This ensures maximum efficiency, allowing standard traffic to pass cleanly while trapping anomalies with progressive computational weight.
|
| 19 |
+
|
| 20 |
+
1. **Layer 0 (Whitelist):** Immediate pass for trusted, verified senders.
|
| 21 |
+
2. **Layer 1 (India Threat Pack):** Hardened heuristic rules targeting specific local threat vectors (see below).
|
| 22 |
+
3. **Layer 2 (Vector Similarity):** Sub-millisecond KNN semantic search against offline scam embeddings using `sqlite-vec` (ARM NEON accelerated).
|
| 23 |
+
4. **Layer 3 (Random Forest):** A lightweight trained ensemble classifier exported to pure Dart via `m2cgen`. Trained on the HuggingFace `india-spam-sms` dataset with 13 custom numerical and categorical features. Scales dynamically during high-risk festival periods.
|
| 24 |
+
5. **Layer 4 (MobileBERT):** Our final deep-learning fallback layer. Integrates the `mrm8488/bert-tiny-finetuned-sms-spam-detection` model running locally via `tflite_flutter` and a custom-built native Dart WordPiece tokenizer.
|
| 25 |
+
|
| 26 |
+
---
|
| 27 |
+
|
| 28 |
+
## 🇮🇳 India Threat Pack
|
| 29 |
+
Premithius is built for the ground reality of Indian digital fraud:
|
| 30 |
+
|
| 31 |
+
* **UPI Firewall:** Natively catches the classic "₹1 collect request" marketplace fraud, scanning for suspicious VPA usage and urgency indicators.
|
| 32 |
+
* **OTP Context Verifier:** Intercepts incoming OTP messages and cross-references them against the active foreground application to prevent OTP-forwarding phishing.
|
| 33 |
+
* **Aadhaar/PAN Data Shield:** A final line of defense baked directly into the custom Premithius Keyboard, warning users *before* they inadvertently send sensitive documents to untrusted contacts.
|
| 34 |
+
* **Festival Calendar:** Scam volume spikes ~340% during major holidays. The pipeline dynamically adjusts its sensitivity multiplier during these critical windows.
|
| 35 |
+
|
| 36 |
+
---
|
| 37 |
+
|
| 38 |
+
## 🚀 Edge-Native & Privacy First
|
| 39 |
+
* **Zero LLM Overhead:** V0.0.3 maintains a tiny footprint by running Random Forest directly in Dart arithmetic and utilizing a highly optimized, quantized BERT module instead of a 900MB Qwen2 model.
|
| 40 |
+
* **Data Shield Insights**: A custom Android native Kotlin telemetry layer continuously and securely queries `AppOpsManager` and `UsageStatsManager` in the background. It surfaces hardware access events (Mic/Camera) and compiles local privacy stories translating background behavior for users.
|
| 41 |
+
* **Home Screen Widget**: A completely native Kotlin AppWidget provides users with 4x2 interactive real-time monitoring of their device's security status, avoiding Flutter engine wakelocks entirely.
|
| 42 |
+
|
| 43 |
+
---
|
| 44 |
+
|
| 45 |
+
## 🛠️ Build Requirements
|
| 46 |
+
|
| 47 |
+
* Flutter SDK (^3.11.1)
|
| 48 |
+
* Android Studio / Android SDK
|
| 49 |
+
* *Note:* The final release build requires the `sqlite-vec-android:0.1.7` dependency. Ensure your Android `build.gradle` is configured with the correct Maven artifact repositories if retrieving `io.github.asg017` fails during compilation.
|
| 50 |
+
|
| 51 |
+
```bash
|
| 52 |
+
# Debug Build
|
| 53 |
+
flutter build apk --debug
|
| 54 |
+
|
| 55 |
+
# Release Build
|
| 56 |
+
flutter build apk --release
|
| 57 |
+
```
|
| 58 |
+
|
| 59 |
+
---
|
| 60 |
+
*Built for the next billion users. Because security shouldn't require surrendering your privacy.*
|
analysis_options.yaml
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file configures the analyzer, which statically analyzes Dart code to
|
| 2 |
+
# check for errors, warnings, and lints.
|
| 3 |
+
#
|
| 4 |
+
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
|
| 5 |
+
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
|
| 6 |
+
# invoked from the command line by running `flutter analyze`.
|
| 7 |
+
|
| 8 |
+
# The following line activates a set of recommended lints for Flutter apps,
|
| 9 |
+
# packages, and plugins designed to encourage good coding practices.
|
| 10 |
+
include: package:flutter_lints/flutter.yaml
|
| 11 |
+
|
| 12 |
+
linter:
|
| 13 |
+
# The lint rules applied to this project can be customized in the
|
| 14 |
+
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
|
| 15 |
+
# included above or to enable additional rules. A list of all available lints
|
| 16 |
+
# and their documentation is published at https://dart.dev/lints.
|
| 17 |
+
#
|
| 18 |
+
# Instead of disabling a lint rule for the entire project in the
|
| 19 |
+
# section below, it can also be suppressed for a single line of code
|
| 20 |
+
# or a specific dart file by using the `// ignore: name_of_lint` and
|
| 21 |
+
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
|
| 22 |
+
# producing the lint.
|
| 23 |
+
rules:
|
| 24 |
+
# avoid_print: false # Uncomment to disable the `avoid_print` rule
|
| 25 |
+
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
|
| 26 |
+
|
| 27 |
+
# Additional information about this file can be found at
|
| 28 |
+
# https://dart.dev/guides/language/analysis-options
|
analyze_current.txt
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Analyzing premithus...
|
| 2 |
+
|
| 3 |
+
info • Don't invoke 'print' in production code • lib/kavacha/kavacha_pipeline.dart:34:5 • avoid_print
|
| 4 |
+
warning • Unused import: 'package:sqflite/sqflite.dart' • lib/kavacha/layers/layer0_whitelist.dart:1:8 • unused_import
|
| 5 |
+
info • Don't invoke 'print' in production code • lib/kavacha/layers/layer4_mobile_bert.dart:15:7 • avoid_print
|
| 6 |
+
info • Parameter 'key' could be a super parameter • lib/keyboard/keyboard_layout.dart:12:9 • use_super_parameters
|
| 7 |
+
info • Invalid use of a private type in a public API • lib/keyboard/keyboard_layout.dart:15:3 • library_private_types_in_public_api
|
| 8 |
+
warning • The value of the field '_hinglish' isn't used • lib/keyboard/keyboard_layout.dart:24:9 • unused_field
|
| 9 |
+
info • Don't use 'BuildContext's across async gaps • lib/keyboard/keyboard_layout.dart:84:35 • use_build_context_synchronously
|
| 10 |
+
info • Parameter 'key' could be a super parameter • lib/keyboard/shortcut_bar.dart:7:9 • use_super_parameters
|
| 11 |
+
info • 'withOpacity' is deprecated and shouldn't be used. Use .withValues() to avoid precision loss • lib/keyboard/shortcut_bar.dart:39:24 • deprecated_member_use
|
| 12 |
+
info • 'withOpacity' is deprecated and shouldn't be used. Use .withValues() to avoid precision loss • lib/keyboard/shortcut_bar.dart:41:43 • deprecated_member_use
|
| 13 |
+
info • Parameter 'key' could be a super parameter • lib/main.dart:57:9 • use_super_parameters
|
| 14 |
+
info • Parameter 'key' could be a super parameter • lib/onboarding/model_download.dart:6:9 • use_super_parameters
|
| 15 |
+
info • Invalid use of a private type in a public API • lib/onboarding/model_download.dart:9:3 • library_private_types_in_public_api
|
| 16 |
+
info • Parameter 'key' could be a super parameter • lib/onboarding/permission_flow.dart:6:9 • use_super_parameters
|
| 17 |
+
info • Invalid use of a private type in a public API • lib/onboarding/permission_flow.dart:9:3 • library_private_types_in_public_api
|
| 18 |
+
info • Parameter 'key' could be a super parameter • lib/screens/data_shield_screen.dart:4:9 • use_super_parameters
|
| 19 |
+
info • 'withOpacity' is deprecated and shouldn't be used. Use .withValues() to avoid precision loss • lib/screens/data_shield_screen.dart:73:33 • deprecated_member_use
|
| 20 |
+
info • 'withOpacity' is deprecated and shouldn't be used. Use .withValues() to avoid precision loss • lib/screens/data_shield_screen.dart:83:36 • deprecated_member_use
|
| 21 |
+
info • Don't invoke 'print' in production code • lib/services/grammar_service.dart:14:5 • avoid_print
|
| 22 |
+
info • The private field _isInit could be 'final' • lib/services/llm_service.dart:8:8 • prefer_final_fields
|
| 23 |
+
warning • The value of the field '_isInit' isn't used • lib/services/llm_service.dart:8:8 • unused_field
|
| 24 |
+
warning • Unused import: 'package:flutter/services.dart' • lib/services/scam_service.dart:1:8 • unused_import
|
| 25 |
+
warning • Unused import: 'package:tflite_flutter/tflite_flutter.dart' • lib/services/scam_service.dart:2:8 • unused_import
|
| 26 |
+
info • Don't invoke 'print' in production code • lib/services/scam_service.dart:38:7 • avoid_print
|
| 27 |
+
info • Don't invoke 'print' in production code • lib/services/scam_service.dart:40:7 • avoid_print
|
| 28 |
+
info • Don't invoke 'print' in production code • lib/services/scam_service.dart:62:9 • avoid_print
|
| 29 |
+
info • Don't invoke 'print' in production code • lib/services/scam_service.dart:86:7 • avoid_print
|
| 30 |
+
info • The imported package 'path' isn't a dependency of the importing package • lib/services/storage_service.dart:2:8 • depend_on_referenced_packages
|
| 31 |
+
info • The imported package 'path' isn't a dependency of the importing package • lib/services/vector_service.dart:4:8 • depend_on_referenced_packages
|
| 32 |
+
warning • Unused import: 'package:path/path.dart' • lib/services/vector_service.dart:4:8 • unused_import
|
| 33 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:57:7 • avoid_print
|
| 34 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:59:7 • avoid_print
|
| 35 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:66:5 • avoid_print
|
| 36 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:78:5 • avoid_print
|
| 37 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:113:7 • avoid_print
|
| 38 |
+
|
| 39 |
+
35 issues found. (ran in 4.7s)
|
analyze_final.txt
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Analyzing premithus...
|
| 2 |
+
|
| 3 |
+
info • Don't invoke 'print' in production code • lib/kavacha/kavacha_pipeline.dart:34:5 • avoid_print
|
| 4 |
+
warning • Unused import: 'package:sqflite/sqflite.dart' • lib/kavacha/layers/layer0_whitelist.dart:1:8 • unused_import
|
| 5 |
+
info • Don't invoke 'print' in production code • lib/kavacha/layers/layer4_mobile_bert.dart:15:7 • avoid_print
|
| 6 |
+
info • Parameter 'key' could be a super parameter • lib/keyboard/keyboard_layout.dart:13:9 • use_super_parameters
|
| 7 |
+
info • Invalid use of a private type in a public API • lib/keyboard/keyboard_layout.dart:16:3 • library_private_types_in_public_api
|
| 8 |
+
warning • The value of the field '_hinglish' isn't used • lib/keyboard/keyboard_layout.dart:25:9 • unused_field
|
| 9 |
+
info • Don't use 'BuildContext's across async gaps • lib/keyboard/keyboard_layout.dart:85:35 • use_build_context_synchronously
|
| 10 |
+
info • Parameter 'key' could be a super parameter • lib/keyboard/shortcut_bar.dart:9:9 • use_super_parameters
|
| 11 |
+
info • Parameter 'key' could be a super parameter • lib/main.dart:57:9 • use_super_parameters
|
| 12 |
+
info • Parameter 'key' could be a super parameter • lib/onboarding/model_download.dart:6:9 • use_super_parameters
|
| 13 |
+
info • Invalid use of a private type in a public API • lib/onboarding/model_download.dart:9:3 • library_private_types_in_public_api
|
| 14 |
+
info • Invalid use of a private type in a public API • lib/onboarding/permission_flow.dart:11:3 • library_private_types_in_public_api
|
| 15 |
+
info • Don't invoke 'print' in production code • lib/services/grammar_service.dart:14:5 • avoid_print
|
| 16 |
+
info • The private field _isInit could be 'final' • lib/services/llm_service.dart:8:8 • prefer_final_fields
|
| 17 |
+
warning • The value of the field '_isInit' isn't used • lib/services/llm_service.dart:8:8 • unused_field
|
| 18 |
+
warning • Duplicate import • lib/services/scam_service.dart:5:8 • duplicate_import
|
| 19 |
+
warning • Duplicate import • lib/services/scam_service.dart:6:8 • duplicate_import
|
| 20 |
+
error • A try block must be followed by an 'on', 'catch', or 'finally' clause • lib/services/scam_service.dart:35:5 • missing_catch_or_finally
|
| 21 |
+
error • Expected to find '}' • lib/services/scam_service.dart:38:3 • expected_token
|
| 22 |
+
info • Don't invoke 'print' in production code • lib/services/scam_service.dart:82:7 • avoid_print
|
| 23 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:56:7 • avoid_print
|
| 24 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:58:7 • avoid_print
|
| 25 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:65:5 • avoid_print
|
| 26 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:77:5 • avoid_print
|
| 27 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:112:7 • avoid_print
|
| 28 |
+
|
| 29 |
+
25 issues found. (ran in 3.5s)
|
analyze_out.txt
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Analyzing premithus...
|
| 2 |
+
|
| 3 |
+
error • The element type 'Layer3RandomForest' can't be assigned to the list type 'KavachaLayer' • lib/kavacha/kavacha_pipeline.dart:29:7 • list_element_type_not_assignable
|
| 4 |
+
info • Don't invoke 'print' in production code • lib/kavacha/kavacha_pipeline.dart:34:5 • avoid_print
|
| 5 |
+
warning • Unused import: 'package:sqflite/sqflite.dart' • lib/kavacha/layers/layer0_whitelist.dart:1:8 • unused_import
|
| 6 |
+
error • Classes and mixins can only implement other classes and mixins • lib/kavacha/layers/layer3_random_forest.dart:5:37 • implements_non_class
|
| 7 |
+
error • The name 'LayerResult' isn't a type, so it can't be used as a type argument • lib/kavacha/layers/layer3_random_forest.dart:9:10 • non_type_as_type_argument
|
| 8 |
+
warning • The method doesn't override an inherited method • lib/kavacha/layers/layer3_random_forest.dart:9:23 • override_on_non_overriding_member
|
| 9 |
+
error • Undefined class 'SmsContext' • lib/kavacha/layers/layer3_random_forest.dart:9:31 • undefined_class
|
| 10 |
+
error • Undefined name 'LayerResult' • lib/kavacha/layers/layer3_random_forest.dart:14:14 • undefined_identifier
|
| 11 |
+
error • Undefined name 'LayerResult' • lib/kavacha/layers/layer3_random_forest.dart:17:14 • undefined_identifier
|
| 12 |
+
error • Undefined name 'LayerResult' • lib/kavacha/layers/layer3_random_forest.dart:21:12 • undefined_identifier
|
| 13 |
+
error • Undefined class 'SmsContext' • lib/kavacha/layers/layer3_random_forest.dart:24:37 • undefined_class
|
| 14 |
+
info • Don't invoke 'print' in production code • lib/kavacha/layers/layer4_mobile_bert.dart:15:7 • avoid_print
|
| 15 |
+
info • Parameter 'key' could be a super parameter • lib/keyboard/keyboard_layout.dart:12:9 • use_super_parameters
|
| 16 |
+
info • Invalid use of a private type in a public API • lib/keyboard/keyboard_layout.dart:15:3 • library_private_types_in_public_api
|
| 17 |
+
warning • The value of the field '_hinglish' isn't used • lib/keyboard/keyboard_layout.dart:23:9 • unused_field
|
| 18 |
+
error • Undefined name '_grammarService' • lib/keyboard/keyboard_layout.dart:28:5 • undefined_identifier
|
| 19 |
+
error • Undefined name '_grammarService' • lib/keyboard/keyboard_layout.dart:39:23 • undefined_identifier
|
| 20 |
+
info • Don't use 'BuildContext's across async gaps • lib/keyboard/keyboard_layout.dart:83:35 • use_build_context_synchronously
|
| 21 |
+
info • Parameter 'key' could be a super parameter • lib/keyboard/shortcut_bar.dart:7:9 • use_super_parameters
|
| 22 |
+
info • 'withOpacity' is deprecated and shouldn't be used. Use .withValues() to avoid precision loss • lib/keyboard/shortcut_bar.dart:39:24 • deprecated_member_use
|
| 23 |
+
info • 'withOpacity' is deprecated and shouldn't be used. Use .withValues() to avoid precision loss • lib/keyboard/shortcut_bar.dart:41:43 • deprecated_member_use
|
| 24 |
+
info • Parameter 'key' could be a super parameter • lib/main.dart:57:9 • use_super_parameters
|
| 25 |
+
info • Parameter 'key' could be a super parameter • lib/onboarding/model_download.dart:6:9 • use_super_parameters
|
| 26 |
+
info • Invalid use of a private type in a public API • lib/onboarding/model_download.dart:9:3 • library_private_types_in_public_api
|
| 27 |
+
info • Parameter 'key' could be a super parameter • lib/onboarding/permission_flow.dart:6:9 • use_super_parameters
|
| 28 |
+
info • Invalid use of a private type in a public API • lib/onboarding/permission_flow.dart:9:3 • library_private_types_in_public_api
|
| 29 |
+
info • Parameter 'key' could be a super parameter • lib/screens/data_shield_screen.dart:4:9 • use_super_parameters
|
| 30 |
+
info • 'withOpacity' is deprecated and shouldn't be used. Use .withValues() to avoid precision loss • lib/screens/data_shield_screen.dart:73:33 • deprecated_member_use
|
| 31 |
+
info • 'withOpacity' is deprecated and shouldn't be used. Use .withValues() to avoid precision loss • lib/screens/data_shield_screen.dart:83:36 • deprecated_member_use
|
| 32 |
+
info • Don't invoke 'print' in production code • lib/services/grammar_service.dart:14:5 • avoid_print
|
| 33 |
+
info • The imported package 'llama_sdk' isn't a dependency of the importing package • lib/services/llm_service.dart:2:8 • depend_on_referenced_packages
|
| 34 |
+
error • Target of URI doesn't exist: 'package:llama_sdk/llama_sdk.dart' • lib/services/llm_service.dart:2:8 • uri_does_not_exist
|
| 35 |
+
info • The private field _isDownloading could be 'final' • lib/services/llm_service.dart:12:8 • prefer_final_fields
|
| 36 |
+
warning • The value of the field '_isDownloading' isn't used • lib/services/llm_service.dart:12:8 • unused_field
|
| 37 |
+
error • Undefined class 'Llama' • lib/services/llm_service.dart:14:3 • undefined_class
|
| 38 |
+
error • The method 'Llama' isn't defined for the type 'LlmService' • lib/services/llm_service.dart:26:16 • undefined_method
|
| 39 |
+
error • The method 'LlamaController' isn't defined for the type 'LlmService' • lib/services/llm_service.dart:26:22 • undefined_method
|
| 40 |
+
info • Don't invoke 'print' in production code • lib/services/llm_service.dart:29:7 • avoid_print
|
| 41 |
+
info • Don't invoke 'print' in production code • lib/services/llm_service.dart:31:7 • avoid_print
|
| 42 |
+
info • Don't invoke 'print' in production code • lib/services/llm_service.dart:45:5 • avoid_print
|
| 43 |
+
error • The method 'UserLlamaMessage' isn't defined for the type 'LlmService' • lib/services/llm_service.dart:67:25 • undefined_method
|
| 44 |
+
info • Don't invoke 'print' in production code • lib/services/llm_service.dart:75:7 • avoid_print
|
| 45 |
+
warning • Unused import: 'package:flutter/services.dart' • lib/services/scam_service.dart:1:8 • unused_import
|
| 46 |
+
warning • Unused import: 'package:tflite_flutter/tflite_flutter.dart' • lib/services/scam_service.dart:2:8 • unused_import
|
| 47 |
+
error • Directives must appear before any declarations • lib/services/scam_service.dart:21:3 • directive_after_declaration
|
| 48 |
+
error • Directives must appear before any declarations • lib/services/scam_service.dart:22:1 • directive_after_declaration
|
| 49 |
+
info • Don't invoke 'print' in production code • lib/services/scam_service.dart:39:7 • avoid_print
|
| 50 |
+
info • Don't invoke 'print' in production code • lib/services/scam_service.dart:41:7 • avoid_print
|
| 51 |
+
info • Don't invoke 'print' in production code • lib/services/scam_service.dart:63:9 • avoid_print
|
| 52 |
+
info • Don't invoke 'print' in production code • lib/services/scam_service.dart:87:7 • avoid_print
|
| 53 |
+
info • The imported package 'path' isn't a dependency of the importing package • lib/services/storage_service.dart:2:8 • depend_on_referenced_packages
|
| 54 |
+
info • The imported package 'path' isn't a dependency of the importing package • lib/services/vector_service.dart:4:8 • depend_on_referenced_packages
|
| 55 |
+
warning • Unused import: 'package:path/path.dart' • lib/services/vector_service.dart:4:8 • unused_import
|
| 56 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:57:7 • avoid_print
|
| 57 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:59:7 • avoid_print
|
| 58 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:66:5 • avoid_print
|
| 59 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:78:5 • avoid_print
|
| 60 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:113:7 • avoid_print
|
| 61 |
+
warning • The asset directory 'models/' doesn't exist • pubspec.yaml:74:7 • asset_directory_does_not_exist
|
| 62 |
+
error • The name 'MyApp' isn't a class • test/widget_test.dart:16:35 • creation_with_non_type
|
| 63 |
+
|
analyze_verify.txt
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Analyzing premithus...
|
| 2 |
+
|
| 3 |
+
info • Don't invoke 'print' in production code • lib/kavacha/kavacha_pipeline.dart:34:5 • avoid_print
|
| 4 |
+
warning • Unused import: 'package:sqflite/sqflite.dart' • lib/kavacha/layers/layer0_whitelist.dart:1:8 • unused_import
|
| 5 |
+
info • Don't invoke 'print' in production code • lib/kavacha/layers/layer4_mobile_bert.dart:15:7 • avoid_print
|
| 6 |
+
info • Parameter 'key' could be a super parameter • lib/keyboard/keyboard_layout.dart:13:9 • use_super_parameters
|
| 7 |
+
info • Invalid use of a private type in a public API • lib/keyboard/keyboard_layout.dart:16:3 • library_private_types_in_public_api
|
| 8 |
+
warning • The value of the field '_hinglish' isn't used • lib/keyboard/keyboard_layout.dart:25:9 • unused_field
|
| 9 |
+
info • Don't use 'BuildContext's across async gaps • lib/keyboard/keyboard_layout.dart:85:35 • use_build_context_synchronously
|
| 10 |
+
info • Parameter 'key' could be a super parameter • lib/keyboard/shortcut_bar.dart:9:9 • use_super_parameters
|
| 11 |
+
info • Parameter 'key' could be a super parameter • lib/main.dart:57:9 • use_super_parameters
|
| 12 |
+
info • Parameter 'key' could be a super parameter • lib/onboarding/model_download.dart:6:9 • use_super_parameters
|
| 13 |
+
info • Invalid use of a private type in a public API • lib/onboarding/model_download.dart:9:3 • library_private_types_in_public_api
|
| 14 |
+
info • Invalid use of a private type in a public API • lib/onboarding/permission_flow.dart:11:3 • library_private_types_in_public_api
|
| 15 |
+
info • Don't invoke 'print' in production code • lib/services/grammar_service.dart:14:5 • avoid_print
|
| 16 |
+
info • The private field _isInit could be 'final' • lib/services/llm_service.dart:8:8 • prefer_final_fields
|
| 17 |
+
warning • The value of the field '_isInit' isn't used • lib/services/llm_service.dart:8:8 • unused_field
|
| 18 |
+
info • Don't invoke 'print' in production code • lib/services/scam_service.dart:83:7 • avoid_print
|
| 19 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:56:7 • avoid_print
|
| 20 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:58:7 • avoid_print
|
| 21 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:65:5 • avoid_print
|
| 22 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:77:5 • avoid_print
|
| 23 |
+
info • Don't invoke 'print' in production code • lib/services/vector_service.dart:112:7 • avoid_print
|
| 24 |
+
|
| 25 |
+
21 issues found. (ran in 3.3s)
|
android/.gitignore
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
gradle-wrapper.jar
|
| 2 |
+
/.gradle
|
| 3 |
+
/captures/
|
| 4 |
+
/gradlew
|
| 5 |
+
/gradlew.bat
|
| 6 |
+
/local.properties
|
| 7 |
+
GeneratedPluginRegistrant.java
|
| 8 |
+
.cxx/
|
| 9 |
+
|
| 10 |
+
# Remember to never publicly share your keystore.
|
| 11 |
+
# See https://flutter.dev/to/reference-keystore
|
| 12 |
+
key.properties
|
| 13 |
+
**/*.keystore
|
| 14 |
+
**/*.jks
|
android/app/build.gradle.kts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
plugins {
|
| 2 |
+
id("com.android.application")
|
| 3 |
+
id("kotlin-android")
|
| 4 |
+
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
| 5 |
+
id("dev.flutter.flutter-gradle-plugin")
|
| 6 |
+
}
|
| 7 |
+
|
| 8 |
+
android {
|
| 9 |
+
namespace = "in.inmodel.premithus"
|
| 10 |
+
compileSdk = flutter.compileSdkVersion
|
| 11 |
+
ndkVersion = flutter.ndkVersion
|
| 12 |
+
|
| 13 |
+
compileOptions {
|
| 14 |
+
sourceCompatibility = JavaVersion.VERSION_17
|
| 15 |
+
targetCompatibility = JavaVersion.VERSION_17
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
kotlinOptions {
|
| 19 |
+
jvmTarget = JavaVersion.VERSION_17.toString()
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
defaultConfig {
|
| 23 |
+
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
| 24 |
+
applicationId = "in.inmodel.premithus"
|
| 25 |
+
// You can update the following values to match your application needs.
|
| 26 |
+
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
| 27 |
+
minSdk = flutter.minSdkVersion
|
| 28 |
+
targetSdk = flutter.targetSdkVersion
|
| 29 |
+
versionCode = flutter.versionCode
|
| 30 |
+
versionName = flutter.versionName
|
| 31 |
+
|
| 32 |
+
ndk {
|
| 33 |
+
abiFilters += listOf("arm64-v8a")
|
| 34 |
+
}
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
buildTypes {
|
| 38 |
+
release {
|
| 39 |
+
// TODO: Add your own signing config for the release build.
|
| 40 |
+
// Signing with the debug keys for now, so `flutter run --release` works.
|
| 41 |
+
signingConfig = signingConfigs.getByName("debug")
|
| 42 |
+
}
|
| 43 |
+
}
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
flutter {
|
| 47 |
+
source = "../.."
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
dependencies {
|
| 51 |
+
// sqlite-vec bundled directly in jniLibs/arm64-v8a/libsqlitevec.so
|
| 52 |
+
// No Gradle dependency needed
|
| 53 |
+
}
|
android/app/src/debug/AndroidManifest.xml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
| 2 |
+
<!-- The INTERNET permission is required for development. Specifically,
|
| 3 |
+
the Flutter tool needs it to communicate with the running application
|
| 4 |
+
to allow setting breakpoints, to provide hot reload, etc.
|
| 5 |
+
-->
|
| 6 |
+
<uses-permission android:name="android.permission.INTERNET"/>
|
| 7 |
+
</manifest>
|
android/app/src/main/AndroidManifest.xml
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
| 2 |
+
xmlns:tools="http://schemas.android.com/tools">
|
| 3 |
+
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
|
| 4 |
+
<uses-permission android:name="android.permission.READ_SMS"/>
|
| 5 |
+
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
| 6 |
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
| 7 |
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
| 8 |
+
<uses-permission android:name="android.permission.INTERNET"/>
|
| 9 |
+
|
| 10 |
+
<application
|
| 11 |
+
android:label="premithus"
|
| 12 |
+
android:name="${applicationName}"
|
| 13 |
+
android:icon="@mipmap/ic_launcher">
|
| 14 |
+
<activity
|
| 15 |
+
android:name=".MainActivity"
|
| 16 |
+
android:exported="true"
|
| 17 |
+
android:launchMode="singleTop"
|
| 18 |
+
android:taskAffinity=""
|
| 19 |
+
android:theme="@style/LaunchTheme"
|
| 20 |
+
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
| 21 |
+
android:hardwareAccelerated="true"
|
| 22 |
+
android:windowSoftInputMode="adjustResize">
|
| 23 |
+
<!-- Specifies an Android theme to apply to this Activity as soon as
|
| 24 |
+
the Android process has started. This theme is visible to the user
|
| 25 |
+
while the Flutter UI initializes. After that, this theme continues
|
| 26 |
+
to determine the Window background behind the Flutter UI. -->
|
| 27 |
+
<meta-data
|
| 28 |
+
android:name="io.flutter.embedding.android.NormalTheme"
|
| 29 |
+
android:resource="@style/NormalTheme"
|
| 30 |
+
/>
|
| 31 |
+
<intent-filter>
|
| 32 |
+
<action android:name="android.intent.action.MAIN"/>
|
| 33 |
+
<category android:name="android.intent.category.LAUNCHER"/>
|
| 34 |
+
</intent-filter>
|
| 35 |
+
</activity>
|
| 36 |
+
|
| 37 |
+
<service android:name=".KeyboardIMEService"
|
| 38 |
+
android:permission="android.permission.BIND_INPUT_METHOD"
|
| 39 |
+
android:exported="true">
|
| 40 |
+
<intent-filter>
|
| 41 |
+
<action android:name="android.view.InputMethod"/>
|
| 42 |
+
</intent-filter>
|
| 43 |
+
<meta-data android:name="android.view.im" android:resource="@xml/method"/>
|
| 44 |
+
</service>
|
| 45 |
+
|
| 46 |
+
<receiver
|
| 47 |
+
android:name=".SmsReceiver"
|
| 48 |
+
android:exported="true"
|
| 49 |
+
android:permission="android.permission.BROADCAST_SMS">
|
| 50 |
+
<intent-filter android:priority="999">
|
| 51 |
+
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
|
| 52 |
+
</intent-filter>
|
| 53 |
+
</receiver>
|
| 54 |
+
|
| 55 |
+
<service
|
| 56 |
+
android:name=".OverlayService"
|
| 57 |
+
android:exported="false"
|
| 58 |
+
android:foregroundServiceType="specialUse" />
|
| 59 |
+
|
| 60 |
+
<receiver android:name=".PremithiusWidget" android:exported="true">
|
| 61 |
+
<intent-filter>
|
| 62 |
+
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
| 63 |
+
</intent-filter>
|
| 64 |
+
<meta-data
|
| 65 |
+
android:name="android.appwidget.provider"
|
| 66 |
+
android:resource="@xml/premithius_widget_info" />
|
| 67 |
+
</receiver>
|
| 68 |
+
|
| 69 |
+
<service
|
| 70 |
+
android:name=".PremithiusAccessibilityService"
|
| 71 |
+
android:exported="true"
|
| 72 |
+
android:label="@string/accessibility_label"
|
| 73 |
+
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
|
| 74 |
+
<intent-filter>
|
| 75 |
+
<action android:name="android.accessibilityservice.AccessibilityService"/>
|
| 76 |
+
</intent-filter>
|
| 77 |
+
<meta-data
|
| 78 |
+
android:name="android.accessibilityservice"
|
| 79 |
+
android:resource="@xml/accessibility_service_config"/>
|
| 80 |
+
</service>
|
| 81 |
+
|
| 82 |
+
<service
|
| 83 |
+
android:name=".KavachaVpnService"
|
| 84 |
+
android:exported="false"
|
| 85 |
+
android:permission="android.permission.BIND_VPN_SERVICE">
|
| 86 |
+
<intent-filter>
|
| 87 |
+
<action android:name="android.net.VpnService"/>
|
| 88 |
+
</intent-filter>
|
| 89 |
+
</service>
|
| 90 |
+
|
| 91 |
+
<!-- Don't delete the meta-data below.
|
| 92 |
+
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
| 93 |
+
<meta-data
|
| 94 |
+
android:name="flutterEmbedding"
|
| 95 |
+
android:value="2" />
|
| 96 |
+
</application>
|
| 97 |
+
<!-- Required to query activities that can process text, see:
|
| 98 |
+
https://developer.android.com/training/package-visibility and
|
| 99 |
+
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
| 100 |
+
|
| 101 |
+
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
| 102 |
+
<queries>
|
| 103 |
+
<intent>
|
| 104 |
+
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
| 105 |
+
<data android:mimeType="text/plain"/>
|
| 106 |
+
</intent>
|
| 107 |
+
</queries>
|
| 108 |
+
</manifest>
|
android/app/src/main/kotlin/in/inmodel/premithus/DomainBlocker.kt
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package `in`.inmodel.premithus
|
| 2 |
+
|
| 3 |
+
import android.content.Context
|
| 4 |
+
import android.util.Log
|
| 5 |
+
|
| 6 |
+
class DomainBlocker(private val context: Context) {
|
| 7 |
+
|
| 8 |
+
private val exactBlocklist = HashSet<String>()
|
| 9 |
+
private val regexPatterns = mutableListOf<Pair<Regex, String>>()
|
| 10 |
+
private val trustedDomains = HashSet<String>()
|
| 11 |
+
|
| 12 |
+
// Known legitimate Indian bank and payment domains
|
| 13 |
+
private val trustedBankDomains = setOf(
|
| 14 |
+
"hdfcbank.com", "netbanking.hdfcbank.com",
|
| 15 |
+
"sbi.co.in", "onlinesbi.sbi.co.in",
|
| 16 |
+
"icicibank.com", "imobileapp.icicibank.com",
|
| 17 |
+
"axisbank.com", "axisnet.axisbank.com",
|
| 18 |
+
"paytm.com", "secure.paytm.com",
|
| 19 |
+
"phonepe.com", "api.phonepe.com",
|
| 20 |
+
"npci.org.in", "upi.npci.org.in",
|
| 21 |
+
"bhimupi.org.in",
|
| 22 |
+
"gpay.app", "pay.google.com",
|
| 23 |
+
"amazon.in", "pay.amazon.in",
|
| 24 |
+
"flipkart.com",
|
| 25 |
+
"swiggy.com", "zomato.com",
|
| 26 |
+
"irctc.co.in", "indianrail.gov.in",
|
| 27 |
+
"incometax.gov.in", "tin.tin.nsdl.com",
|
| 28 |
+
"uidai.gov.in",
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
init {
|
| 32 |
+
loadBlocklists()
|
| 33 |
+
trustedDomains.addAll(trustedBankDomains)
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
private fun loadBlocklists() {
|
| 37 |
+
// Load seed blocklist from assets
|
| 38 |
+
try {
|
| 39 |
+
context.assets.open("blocklist/india_scam_domains.txt")
|
| 40 |
+
.bufferedReader()
|
| 41 |
+
.forEachLine { line ->
|
| 42 |
+
val clean = line.trim().lowercase()
|
| 43 |
+
if (clean.isNotEmpty() && !clean.startsWith("#")) {
|
| 44 |
+
exactBlocklist.add(clean)
|
| 45 |
+
}
|
| 46 |
+
}
|
| 47 |
+
Log.d("DomainBlocker", "Loaded ${exactBlocklist.size} blocked domains")
|
| 48 |
+
} catch (e: Exception) {
|
| 49 |
+
Log.e("DomainBlocker", "Failed to load blocklist: \${e.message}")
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
// Load user-confirmed blocked domains from SQLite via direct path since Flutter owns DB
|
| 53 |
+
try {
|
| 54 |
+
val dbPath = context.getDatabasePath("premithius.db").absolutePath
|
| 55 |
+
val dbFile = java.io.File(dbPath)
|
| 56 |
+
if (dbFile.exists()) {
|
| 57 |
+
val db = android.database.sqlite.SQLiteDatabase.openDatabase(dbPath, null, android.database.sqlite.SQLiteDatabase.OPEN_READONLY)
|
| 58 |
+
val cursor = db.rawQuery("SELECT domain FROM scam_domains", null)
|
| 59 |
+
while (cursor.moveToNext()) {
|
| 60 |
+
exactBlocklist.add(cursor.getString(0).lowercase())
|
| 61 |
+
}
|
| 62 |
+
cursor.close()
|
| 63 |
+
db.close()
|
| 64 |
+
}
|
| 65 |
+
} catch (e: Exception) {
|
| 66 |
+
Log.e("DomainBlocker", "Failed to load user domains: \${e.message}")
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
// Regex patterns for structural scam detection
|
| 70 |
+
regexPatterns.addAll(listOf(
|
| 71 |
+
// Suspicious TLDs
|
| 72 |
+
Regex(""".*\.(xyz|top|click|tk|ml|ga|cf|gq|pw|cc|su)$""") to "Suspicious TLD",
|
| 73 |
+
|
| 74 |
+
// Typosquat: known bank name + extra chars
|
| 75 |
+
Regex(""".*hdfc.*bank.*(?!hdfcbank\.com)""") to "HDFC Typosquat",
|
| 76 |
+
Regex(""".*sbi.*(?!sbi\.co\.in|onlinesbi)""") to "SBI Typosquat",
|
| 77 |
+
Regex(""".*icici.*(?!icicibank\.com)""") to "ICICI Typosquat",
|
| 78 |
+
Regex(""".*paytm.*(?!paytm\.com)""") to "Paytm Typosquat",
|
| 79 |
+
Regex(""".*phonepe.*(?!phonepe\.com)""") to "PhonePe Typosquat",
|
| 80 |
+
|
| 81 |
+
// KYC fraud patterns
|
| 82 |
+
Regex(""".*kyc.*(update|verify|expire|block).*""") to "KYC Fraud",
|
| 83 |
+
Regex(""".*verify.*(kyc|account|aadhar|aadhaar).*""") to "KYC Fraud",
|
| 84 |
+
|
| 85 |
+
// UPI fraud patterns
|
| 86 |
+
Regex(""".*upi.*(reward|prize|cashback|bonus).*""") to "UPI Fraud",
|
| 87 |
+
Regex(""".*collect.*(rs|rupee|inr|money).*""") to "UPI Collect Fraud",
|
| 88 |
+
|
| 89 |
+
// Generic phishing patterns
|
| 90 |
+
Regex(""".*secure.*login.*\.(xyz|net|org|in|co)$""") to "Phishing",
|
| 91 |
+
Regex(""".*account.*(blocked|suspended|expired).*""") to "Account Fraud",
|
| 92 |
+
))
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
fun check(domain: String): BlockResult {
|
| 96 |
+
val d = domain.lowercase().trimEnd('.')
|
| 97 |
+
|
| 98 |
+
// Step 1: Trusted domain — always allow instantly
|
| 99 |
+
if (trustedDomains.any { d == it || d.endsWith(".$it") }) {
|
| 100 |
+
return BlockResult(isBlocked = false, domain = d, category = "")
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
// Step 2: Exact match in blocklist
|
| 104 |
+
if (exactBlocklist.contains(d)) {
|
| 105 |
+
return BlockResult(isBlocked = true, domain = d, category = "Known Scam Domain")
|
| 106 |
+
}
|
| 107 |
+
|
| 108 |
+
// Step 3: Levenshtein distance check against trusted bank domains
|
| 109 |
+
// Catches: hdfcbannk.com (typo), hdfcbank-kyc.net (subdomain fraud)
|
| 110 |
+
for (trusted in trustedBankDomains) {
|
| 111 |
+
val baseTrusted = trusted.substringBefore(".")
|
| 112 |
+
val baseQuery = d.substringBefore(".")
|
| 113 |
+
if (levenshtein(baseQuery, baseTrusted) in 1..2) {
|
| 114 |
+
return BlockResult(isBlocked = true, domain = d,
|
| 115 |
+
category = "Typosquat — similar to $trusted")
|
| 116 |
+
}
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
// Step 4: Regex pattern matching
|
| 120 |
+
for ((pattern, category) in regexPatterns) {
|
| 121 |
+
// Note: need to handle regex exclusion properly. Since patterns above use negative lookahead incorrectly for exact strings, we might want to ensure we don't block valid domains.
|
| 122 |
+
// Actually the negative lookahead matches at current position. Because Android Kotlin PCRE works.
|
| 123 |
+
if (pattern.containsMatchIn(d)) {
|
| 124 |
+
return BlockResult(isBlocked = true, domain = d, category = category)
|
| 125 |
+
}
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
+
// Step 5: Allow — not in any blocklist
|
| 129 |
+
return BlockResult(isBlocked = false, domain = d, category = "")
|
| 130 |
+
}
|
| 131 |
+
|
| 132 |
+
// Levenshtein distance — pure Kotlin, no library needed
|
| 133 |
+
private fun levenshtein(a: String, b: String): Int {
|
| 134 |
+
val dp = Array(a.length + 1) { IntArray(b.length + 1) }
|
| 135 |
+
for (i in 0..a.length) dp[i][0] = i
|
| 136 |
+
for (j in 0..b.length) dp[0][j] = j
|
| 137 |
+
for (i in 1..a.length) {
|
| 138 |
+
for (j in 1..b.length) {
|
| 139 |
+
dp[i][j] = if (a[i-1] == b[j-1]) dp[i-1][j-1]
|
| 140 |
+
else 1 + minOf(dp[i-1][j], dp[i][j-1], dp[i-1][j-1])
|
| 141 |
+
}
|
| 142 |
+
}
|
| 143 |
+
return dp[a.length][b.length]
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
// Reload user-confirmed domains from SQLite
|
| 147 |
+
// Call after user confirms a scam domain
|
| 148 |
+
fun reload() {
|
| 149 |
+
exactBlocklist.clear()
|
| 150 |
+
regexPatterns.clear()
|
| 151 |
+
loadBlocklists()
|
| 152 |
+
}
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
data class BlockResult(
|
| 156 |
+
val isBlocked: Boolean,
|
| 157 |
+
val domain: String,
|
| 158 |
+
val category: String
|
| 159 |
+
)
|
android/app/src/main/kotlin/in/inmodel/premithus/KavachaVpnService.kt
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package `in`.inmodel.premithus
|
| 2 |
+
|
| 3 |
+
import android.content.Intent
|
| 4 |
+
import android.net.VpnService
|
| 5 |
+
import android.os.ParcelFileDescriptor
|
| 6 |
+
import android.util.Log
|
| 7 |
+
import java.io.FileInputStream
|
| 8 |
+
import java.io.FileOutputStream
|
| 9 |
+
import java.net.DatagramPacket
|
| 10 |
+
import java.net.DatagramSocket
|
| 11 |
+
import java.net.InetAddress
|
| 12 |
+
|
| 13 |
+
class KavachaVpnService : VpnService() {
|
| 14 |
+
|
| 15 |
+
companion object {
|
| 16 |
+
const val ACTION_START = "ACTION_VPN_START"
|
| 17 |
+
const val ACTION_STOP = "ACTION_VPN_STOP"
|
| 18 |
+
const val CHANNEL = "in.inmodel.premithius/vpn"
|
| 19 |
+
var isRunning = false
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
private var vpnInterface: ParcelFileDescriptor? = null
|
| 23 |
+
private var tunnelThread: Thread? = null
|
| 24 |
+
private lateinit var domainBlocker: DomainBlocker
|
| 25 |
+
|
| 26 |
+
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
| 27 |
+
if (intent?.action == ACTION_STOP) {
|
| 28 |
+
stopVpn()
|
| 29 |
+
return START_NOT_STICKY
|
| 30 |
+
}
|
| 31 |
+
if (intent?.action == "ACTION_RELOAD_BLOCKLIST") {
|
| 32 |
+
if (::domainBlocker.isInitialized) {
|
| 33 |
+
domainBlocker.reload()
|
| 34 |
+
}
|
| 35 |
+
return START_STICKY
|
| 36 |
+
}
|
| 37 |
+
if (!isRunning) startVpn()
|
| 38 |
+
return START_STICKY
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
private fun startVpn() {
|
| 42 |
+
// Build the TUN interface
|
| 43 |
+
val builder = Builder()
|
| 44 |
+
.setSession("Premithius Kavacha")
|
| 45 |
+
.addAddress("10.0.0.2", 24)
|
| 46 |
+
.addDnsServer("10.0.0.1") // our local DNS resolver
|
| 47 |
+
.addRoute("0.0.0.0", 0) // route all traffic
|
| 48 |
+
.setMtu(1500)
|
| 49 |
+
.setBlocking(true)
|
| 50 |
+
|
| 51 |
+
// Exclude Premithius itself from VPN
|
| 52 |
+
// so our DNS forwarding to 1.1.1.1 is not intercepted
|
| 53 |
+
try {
|
| 54 |
+
builder.addDisallowedApplication(packageName)
|
| 55 |
+
} catch (e: Exception) {
|
| 56 |
+
Log.e("KavachaVPN", "Failed to exclude app from VPN: \${e.message}")
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
vpnInterface = builder.establish()
|
| 60 |
+
isRunning = true
|
| 61 |
+
|
| 62 |
+
domainBlocker = DomainBlocker(this)
|
| 63 |
+
|
| 64 |
+
// Start tunnel on background thread
|
| 65 |
+
tunnelThread = Thread {
|
| 66 |
+
runTunnel()
|
| 67 |
+
}.also { it.start() }
|
| 68 |
+
|
| 69 |
+
Log.d("KavachaVPN", "VPN tunnel started")
|
| 70 |
+
notifyFlutter("started")
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
private fun stopVpn() {
|
| 74 |
+
isRunning = false
|
| 75 |
+
tunnelThread?.interrupt()
|
| 76 |
+
try { vpnInterface?.close() } catch (e: Exception) {}
|
| 77 |
+
vpnInterface = null
|
| 78 |
+
Log.d("KavachaVPN", "VPN tunnel stopped")
|
| 79 |
+
notifyFlutter("stopped")
|
| 80 |
+
stopSelf()
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
private fun runTunnel() {
|
| 84 |
+
val vpnFd = vpnInterface?.fileDescriptor ?: return
|
| 85 |
+
val inputStream = FileInputStream(vpnFd)
|
| 86 |
+
val outputStream = FileOutputStream(vpnFd)
|
| 87 |
+
|
| 88 |
+
val packet = ByteArray(32767)
|
| 89 |
+
|
| 90 |
+
while (isRunning && !Thread.interrupted()) {
|
| 91 |
+
try {
|
| 92 |
+
// Read incoming IP packet
|
| 93 |
+
val length = inputStream.read(packet)
|
| 94 |
+
if (length <= 0) continue
|
| 95 |
+
|
| 96 |
+
// Parse and handle — focus on DNS (UDP port 53)
|
| 97 |
+
val handled = handlePacket(packet, length, outputStream)
|
| 98 |
+
|
| 99 |
+
// If not DNS — pass through unchanged
|
| 100 |
+
if (!handled) outputStream.write(packet, 0, length)
|
| 101 |
+
|
| 102 |
+
} catch (e: InterruptedException) {
|
| 103 |
+
break
|
| 104 |
+
} catch (e: Exception) {
|
| 105 |
+
Log.e("KavachaVPN", "Tunnel error: \${e.message}")
|
| 106 |
+
}
|
| 107 |
+
}
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
private fun handlePacket(
|
| 111 |
+
packet: ByteArray,
|
| 112 |
+
length: Int,
|
| 113 |
+
output: FileOutputStream
|
| 114 |
+
): Boolean {
|
| 115 |
+
// Check if this is an IP packet (version 4)
|
| 116 |
+
if (length < 20) return false
|
| 117 |
+
val ipVersion = (packet[0].toInt() shr 4) and 0xF
|
| 118 |
+
if (ipVersion != 4) return false
|
| 119 |
+
|
| 120 |
+
// Check protocol — 17 = UDP
|
| 121 |
+
val protocol = packet[9].toInt() and 0xFF
|
| 122 |
+
if (protocol != 17) return false
|
| 123 |
+
|
| 124 |
+
// Check destination port — 53 = DNS
|
| 125 |
+
val ipHeaderLen = (packet[0].toInt() and 0xF) * 4
|
| 126 |
+
if (length < ipHeaderLen + 8) return false
|
| 127 |
+
val destPort = ((packet[ipHeaderLen + 2].toInt() and 0xFF) shl 8) or
|
| 128 |
+
(packet[ipHeaderLen + 3].toInt() and 0xFF)
|
| 129 |
+
if (destPort != 53) return false
|
| 130 |
+
|
| 131 |
+
// Extract DNS query payload
|
| 132 |
+
val dnsStart = ipHeaderLen + 8
|
| 133 |
+
val dnsLength = length - dnsStart
|
| 134 |
+
if (dnsLength < 12) return false
|
| 135 |
+
val dnsPayload = packet.copyOfRange(dnsStart, dnsStart + dnsLength)
|
| 136 |
+
|
| 137 |
+
// Parse domain name from DNS query
|
| 138 |
+
val domain = parseDnsQuery(dnsPayload) ?: return false
|
| 139 |
+
|
| 140 |
+
Log.d("KavachaVPN", "DNS query: \$domain")
|
| 141 |
+
|
| 142 |
+
// Check domain against blocklist
|
| 143 |
+
val blockResult = domainBlocker.check(domain)
|
| 144 |
+
|
| 145 |
+
if (blockResult.isBlocked) {
|
| 146 |
+
Log.d("KavachaVPN", "BLOCKED: \$domain — \${blockResult.category}")
|
| 147 |
+
|
| 148 |
+
// Return NXDOMAIN response — domain does not exist
|
| 149 |
+
val nxDomainResponse = buildNxDomainResponse(packet, length, dnsPayload)
|
| 150 |
+
output.write(nxDomainResponse)
|
| 151 |
+
|
| 152 |
+
// Log and alert
|
| 153 |
+
logBlockedDomain(domain, blockResult)
|
| 154 |
+
return true
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
// Safe domain — forward to 1.1.1.1 and return real answer
|
| 158 |
+
val realResponse = forwardDns(dnsPayload, "1.1.1.1")
|
| 159 |
+
if (realResponse != null) {
|
| 160 |
+
val responsePacket = buildResponsePacket(packet, realResponse)
|
| 161 |
+
output.write(responsePacket)
|
| 162 |
+
return true
|
| 163 |
+
}
|
| 164 |
+
|
| 165 |
+
return false
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
private fun logBlockedDomain(domain: String, result: BlockResult) {
|
| 169 |
+
// Log to SQLite (directly into scam_domains + vpn_events)
|
| 170 |
+
Thread {
|
| 171 |
+
try {
|
| 172 |
+
// Assuming path provider defaults for database path
|
| 173 |
+
val dbPath = applicationContext.getDatabasePath("premithius.db").absolutePath
|
| 174 |
+
val db = android.database.sqlite.SQLiteDatabase.openDatabase(dbPath, null, android.database.sqlite.SQLiteDatabase.OPEN_READWRITE)
|
| 175 |
+
|
| 176 |
+
db.execSQL("UPDATE scam_domains SET hit_count = hit_count + 1 WHERE domain = ?", arrayOf(domain))
|
| 177 |
+
|
| 178 |
+
val insertEvent = "INSERT INTO vpn_events (event_type, domain, category, created_at) VALUES (?, ?, ?, ?)"
|
| 179 |
+
db.execSQL(insertEvent, arrayOf("blocked", domain, result.category, System.currentTimeMillis()))
|
| 180 |
+
|
| 181 |
+
db.close()
|
| 182 |
+
} catch (e: Exception) {
|
| 183 |
+
Log.e("KavachaVPN", "Failed to log blocked domain to SQLite: \${e.message}")
|
| 184 |
+
}
|
| 185 |
+
}.start()
|
| 186 |
+
|
| 187 |
+
// Fire overlay alert via OverlayService
|
| 188 |
+
val intent = Intent(this, OverlayService::class.java)
|
| 189 |
+
intent.action = OverlayService.ACTION_SHOW_DOMAIN_BLOCK
|
| 190 |
+
intent.putExtra("domain", domain)
|
| 191 |
+
intent.putExtra("category", result.category)
|
| 192 |
+
startService(intent)
|
| 193 |
+
|
| 194 |
+
// Notify Flutter for VPN stats screen
|
| 195 |
+
notifyFlutter("blocked:\$domain:\${result.category}")
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
private fun notifyFlutter(event: String) {
|
| 199 |
+
// Store in shared prefs — Flutter polls on resume
|
| 200 |
+
// Direct MethodChannel not reliable from background service
|
| 201 |
+
getSharedPreferences("vpn_events", MODE_PRIVATE)
|
| 202 |
+
.edit()
|
| 203 |
+
.putString("last_event", event)
|
| 204 |
+
.putLong("last_event_time", System.currentTimeMillis())
|
| 205 |
+
.apply()
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
override fun onDestroy() {
|
| 209 |
+
stopVpn()
|
| 210 |
+
super.onDestroy()
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
// DNS parsing helpers
|
| 214 |
+
|
| 215 |
+
private fun parseDnsQuery(payload: ByteArray): String? {
|
| 216 |
+
try {
|
| 217 |
+
if (payload.size < 12) return null
|
| 218 |
+
var pos = 12 // Skip DNS header
|
| 219 |
+
val sb = StringBuilder()
|
| 220 |
+
while (pos < payload.size) {
|
| 221 |
+
val len = payload[pos].toInt() and 0xFF
|
| 222 |
+
if (len == 0) break // Root label
|
| 223 |
+
if (len >= 192) return null // Pointer, not supporting compressed query parsing
|
| 224 |
+
pos++
|
| 225 |
+
if (pos + len > payload.size) return null
|
| 226 |
+
for (i in 0 until len) {
|
| 227 |
+
sb.append(payload[pos + i].toInt().toChar())
|
| 228 |
+
}
|
| 229 |
+
sb.append('.')
|
| 230 |
+
pos += len
|
| 231 |
+
}
|
| 232 |
+
return if (sb.isNotEmpty()) sb.toString().trimEnd('.') else null
|
| 233 |
+
} catch (e: Exception) {
|
| 234 |
+
return null
|
| 235 |
+
}
|
| 236 |
+
}
|
| 237 |
+
|
| 238 |
+
private fun buildNxDomainResponse(original: ByteArray, len: Int, dns: ByteArray): ByteArray {
|
| 239 |
+
val response = ByteArray(len)
|
| 240 |
+
System.arraycopy(original, 0, response, 0, len)
|
| 241 |
+
|
| 242 |
+
val ipHeaderLen = (response[0].toInt() and 0xF) * 4
|
| 243 |
+
val udpStart = ipHeaderLen
|
| 244 |
+
|
| 245 |
+
// IP header: Swap Source and Dest IP
|
| 246 |
+
for (i in 0..3) {
|
| 247 |
+
val tmp = response[12 + i]
|
| 248 |
+
response[12 + i] = response[16 + i]
|
| 249 |
+
response[16 + i] = tmp
|
| 250 |
+
}
|
| 251 |
+
|
| 252 |
+
// UDP header: Swap Source and Dest Port
|
| 253 |
+
for (i in 0..1) {
|
| 254 |
+
val tmp = response[udpStart + i]
|
| 255 |
+
response[udpStart + i] = response[udpStart + 2 + i]
|
| 256 |
+
response[udpStart + 2 + i] = tmp
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
// DNS header: QR=1(response), RCODE=3(NXDOMAIN)
|
| 260 |
+
val dnsStart = udpStart + 8
|
| 261 |
+
response[dnsStart + 2] = (response[dnsStart + 2].toInt() or 0x80).toByte() // QR=1
|
| 262 |
+
response[dnsStart + 3] = ((response[dnsStart + 3].toInt() and 0xF0) or 0x03).toByte()
|
| 263 |
+
|
| 264 |
+
// Set ANCOUNT=0, NSCOUNT=0, ARCOUNT=0
|
| 265 |
+
response[dnsStart + 6] = 0; response[dnsStart + 7] = 0
|
| 266 |
+
response[dnsStart + 8] = 0; response[dnsStart + 9] = 0
|
| 267 |
+
response[dnsStart + 10] = 0; response[dnsStart + 11] = 0
|
| 268 |
+
|
| 269 |
+
// Clear UDP checksum
|
| 270 |
+
response[udpStart + 6] = 0
|
| 271 |
+
response[udpStart + 7] = 0
|
| 272 |
+
|
| 273 |
+
return response
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
private fun buildResponsePacket(original: ByteArray, dnsResponse: ByteArray): ByteArray {
|
| 277 |
+
val ipHeaderLen = (original[0].toInt() and 0xF) * 4
|
| 278 |
+
val udpHeaderLen = 8
|
| 279 |
+
val newLen = ipHeaderLen + udpHeaderLen + dnsResponse.size
|
| 280 |
+
val packet = ByteArray(newLen)
|
| 281 |
+
|
| 282 |
+
System.arraycopy(original, 0, packet, 0, ipHeaderLen + udpHeaderLen)
|
| 283 |
+
System.arraycopy(dnsResponse, 0, packet, ipHeaderLen + udpHeaderLen, dnsResponse.size)
|
| 284 |
+
|
| 285 |
+
// IP header Total length
|
| 286 |
+
packet[2] = (newLen shr 8).toByte()
|
| 287 |
+
packet[3] = (newLen and 0xFF).toByte()
|
| 288 |
+
|
| 289 |
+
// Swap IPs
|
| 290 |
+
for (i in 0..3) {
|
| 291 |
+
val tmp = packet[12 + i]
|
| 292 |
+
packet[12 + i] = packet[16 + i]
|
| 293 |
+
packet[16 + i] = tmp
|
| 294 |
+
}
|
| 295 |
+
|
| 296 |
+
// Swap UDP Ports
|
| 297 |
+
val udpStart = ipHeaderLen
|
| 298 |
+
for (i in 0..1) {
|
| 299 |
+
val tmp = packet[udpStart + i]
|
| 300 |
+
packet[udpStart + i] = packet[udpStart + 2 + i]
|
| 301 |
+
packet[udpStart + 2 + i] = tmp
|
| 302 |
+
}
|
| 303 |
+
|
| 304 |
+
// UDP Length
|
| 305 |
+
val udpLen = udpHeaderLen + dnsResponse.size
|
| 306 |
+
packet[udpStart + 4] = (udpLen shr 8).toByte()
|
| 307 |
+
packet[udpStart + 5] = (udpLen and 0xFF).toByte()
|
| 308 |
+
|
| 309 |
+
// Clear UDP checksum
|
| 310 |
+
packet[udpStart + 6] = 0
|
| 311 |
+
packet[udpStart + 7] = 0
|
| 312 |
+
|
| 313 |
+
return packet
|
| 314 |
+
}
|
| 315 |
+
|
| 316 |
+
private fun forwardDns(query: ByteArray, server: String): ByteArray? {
|
| 317 |
+
try {
|
| 318 |
+
val socket = DatagramSocket()
|
| 319 |
+
socket.soTimeout = 3000
|
| 320 |
+
val address = InetAddress.getByName(server)
|
| 321 |
+
val packet = DatagramPacket(query, query.size, address, 53)
|
| 322 |
+
socket.send(packet)
|
| 323 |
+
|
| 324 |
+
val buf = ByteArray(4096)
|
| 325 |
+
val responsePacket = DatagramPacket(buf, buf.size)
|
| 326 |
+
socket.receive(responsePacket)
|
| 327 |
+
socket.close()
|
| 328 |
+
|
| 329 |
+
return buf.copyOfRange(0, responsePacket.length)
|
| 330 |
+
} catch (e: Exception) {
|
| 331 |
+
Log.e("KavachaVPN", "Forward DNS failed: \${e.message}")
|
| 332 |
+
return null
|
| 333 |
+
}
|
| 334 |
+
}
|
| 335 |
+
}
|
android/app/src/main/kotlin/in/inmodel/premithus/KeyboardIMEService.kt
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package `in`.inmodel.premithus
|
| 2 |
+
|
| 3 |
+
import android.inputmethodservice.InputMethodService
|
| 4 |
+
import android.view.View
|
| 5 |
+
import android.widget.FrameLayout
|
| 6 |
+
import io.flutter.FlutterInjector
|
| 7 |
+
import io.flutter.embedding.android.FlutterView
|
| 8 |
+
import io.flutter.embedding.engine.FlutterEngine
|
| 9 |
+
import io.flutter.embedding.engine.dart.DartExecutor
|
| 10 |
+
import io.flutter.plugin.common.MethodChannel
|
| 11 |
+
|
| 12 |
+
class KeyboardIMEService : InputMethodService() {
|
| 13 |
+
|
| 14 |
+
private lateinit var flutterEngine: FlutterEngine
|
| 15 |
+
private lateinit var flutterView: FlutterView
|
| 16 |
+
private lateinit var methodChannel: MethodChannel
|
| 17 |
+
private val CHANNEL = "in.inmodel.premithus/keyboard"
|
| 18 |
+
|
| 19 |
+
override fun onCreate() {
|
| 20 |
+
super.onCreate()
|
| 21 |
+
|
| 22 |
+
flutterEngine = FlutterEngine(this)
|
| 23 |
+
flutterEngine.dartExecutor.executeDartEntrypoint(
|
| 24 |
+
DartExecutor.DartEntrypoint(
|
| 25 |
+
FlutterInjector.instance().flutterLoader().findAppBundlePath(),
|
| 26 |
+
"keyboardMain"
|
| 27 |
+
)
|
| 28 |
+
)
|
| 29 |
+
|
| 30 |
+
methodChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
|
| 31 |
+
methodChannel.setMethodCallHandler { call, result ->
|
| 32 |
+
when (call.method) {
|
| 33 |
+
"commitText" -> {
|
| 34 |
+
val text = call.argument<String>("text") ?: ""
|
| 35 |
+
currentInputConnection?.commitText(text, 1)
|
| 36 |
+
result.success(null)
|
| 37 |
+
}
|
| 38 |
+
"deleteBackward" -> {
|
| 39 |
+
currentInputConnection?.deleteSurroundingText(1, 0)
|
| 40 |
+
result.success(null)
|
| 41 |
+
}
|
| 42 |
+
"replaceText" -> {
|
| 43 |
+
val newText = call.argument<String>("text") ?: ""
|
| 44 |
+
currentInputConnection?.deleteSurroundingText(10000, 10000)
|
| 45 |
+
currentInputConnection?.commitText(newText, 1)
|
| 46 |
+
result.success(null)
|
| 47 |
+
}
|
| 48 |
+
"getText" -> {
|
| 49 |
+
val before = currentInputConnection?.getTextBeforeCursor(5000, 0) ?: ""
|
| 50 |
+
val after = currentInputConnection?.getTextAfterCursor(5000, 0) ?: ""
|
| 51 |
+
result.success(before.toString() + after.toString())
|
| 52 |
+
}
|
| 53 |
+
"hideKeyboard" -> {
|
| 54 |
+
requestHideSelf(0)
|
| 55 |
+
result.success(null)
|
| 56 |
+
}
|
| 57 |
+
else -> result.notImplemented()
|
| 58 |
+
}
|
| 59 |
+
}
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
override fun onCreateInputView(): View {
|
| 63 |
+
flutterView = FlutterView(this)
|
| 64 |
+
flutterView.attachToFlutterEngine(flutterEngine)
|
| 65 |
+
|
| 66 |
+
// Wrap in FrameLayout to ensure layout bounds
|
| 67 |
+
val frameLayout = FrameLayout(this)
|
| 68 |
+
frameLayout.addView(flutterView)
|
| 69 |
+
return frameLayout
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
override fun onDestroy() {
|
| 73 |
+
flutterView.detachFromFlutterEngine()
|
| 74 |
+
flutterEngine.destroy()
|
| 75 |
+
super.onDestroy()
|
| 76 |
+
}
|
| 77 |
+
}
|
android/app/src/main/kotlin/in/inmodel/premithus/LockedAppDao.kt
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package `in`.inmodel.premithus
|
| 2 |
+
|
| 3 |
+
import android.content.ContentValues
|
| 4 |
+
import android.database.sqlite.SQLiteDatabase
|
| 5 |
+
|
| 6 |
+
class LockedAppDao(private val db: SQLiteDatabase) {
|
| 7 |
+
|
| 8 |
+
fun isLocked(packageName: String): Boolean {
|
| 9 |
+
// Check if app is in locked_apps table AND no active temporary unlock
|
| 10 |
+
val cursor = db.rawQuery(
|
| 11 |
+
"SELECT temporary_unlock_until FROM locked_apps WHERE package_name = ?",
|
| 12 |
+
arrayOf(packageName)
|
| 13 |
+
)
|
| 14 |
+
if (!cursor.moveToFirst()) {
|
| 15 |
+
cursor.close()
|
| 16 |
+
return false
|
| 17 |
+
}
|
| 18 |
+
val tempUntil = cursor.getLong(0)
|
| 19 |
+
cursor.close()
|
| 20 |
+
|
| 21 |
+
// If temporary_unlock_until is 0 or in the past, app is locked
|
| 22 |
+
return tempUntil == 0L || System.currentTimeMillis() > tempUntil
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
fun logAccessAttempt(packageName: String, timestamp: Long) {
|
| 26 |
+
// Increment access_attempts in locked_apps
|
| 27 |
+
db.execSQL(
|
| 28 |
+
"UPDATE locked_apps SET access_attempts = access_attempts + 1 WHERE package_name = ?",
|
| 29 |
+
arrayOf(packageName)
|
| 30 |
+
)
|
| 31 |
+
// Insert into access_log
|
| 32 |
+
val values = ContentValues().apply {
|
| 33 |
+
put("package_name", packageName)
|
| 34 |
+
put("was_allowed", 0)
|
| 35 |
+
put("attempted_at", timestamp)
|
| 36 |
+
}
|
| 37 |
+
try {
|
| 38 |
+
db.insert("access_log", null, values)
|
| 39 |
+
} catch (e: Exception) {
|
| 40 |
+
// Table may not exist yet — ignore
|
| 41 |
+
}
|
| 42 |
+
}
|
| 43 |
+
}
|
android/app/src/main/kotlin/in/inmodel/premithus/MainActivity.kt
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package `in`.inmodel.premithus
|
| 2 |
+
|
| 3 |
+
import android.accessibilityservice.AccessibilityServiceInfo
|
| 4 |
+
import android.content.Context
|
| 5 |
+
import android.content.pm.ApplicationInfo
|
| 6 |
+
import android.content.pm.PackageManager
|
| 7 |
+
import android.os.Bundle
|
| 8 |
+
import android.view.accessibility.AccessibilityManager
|
| 9 |
+
import io.flutter.embedding.android.FlutterActivity
|
| 10 |
+
import io.flutter.embedding.engine.FlutterEngine
|
| 11 |
+
import io.flutter.plugin.common.MethodChannel
|
| 12 |
+
import android.content.Intent
|
| 13 |
+
import android.net.VpnService
|
| 14 |
+
import android.os.Handler
|
| 15 |
+
import android.os.Looper
|
| 16 |
+
import org.json.JSONArray
|
| 17 |
+
import org.json.JSONObject
|
| 18 |
+
import kotlin.concurrent.thread
|
| 19 |
+
|
| 20 |
+
class MainActivity : FlutterActivity() {
|
| 21 |
+
|
| 22 |
+
companion object {
|
| 23 |
+
// Accessibility channel — shared with PremithiusAccessibilityService
|
| 24 |
+
var accessibilityChannel: MethodChannel? = null
|
| 25 |
+
|
| 26 |
+
// Auth channel — shared with OverlayService
|
| 27 |
+
var authChannel: MethodChannel? = null
|
| 28 |
+
|
| 29 |
+
const val VPN_REQUEST_CODE = 1001
|
| 30 |
+
var pendingVpnResult: MethodChannel.Result? = null
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
private val alreadyAlerted = mutableSetOf<String>()
|
| 34 |
+
private val sensorMonitorHandler = Handler(Looper.getMainLooper())
|
| 35 |
+
private val sensorMonitorRunnable = object : Runnable {
|
| 36 |
+
override fun run() {
|
| 37 |
+
thread {
|
| 38 |
+
val helper = TelemetryHelper(this@MainActivity)
|
| 39 |
+
val active = try { helper.getActiveSensors() } catch(e:Exception){ emptyList() }
|
| 40 |
+
for (sensor in active) {
|
| 41 |
+
val key = sensor.packageName + sensor.sensor
|
| 42 |
+
if (!alreadyAlerted.contains(key)) {
|
| 43 |
+
alreadyAlerted.add(key)
|
| 44 |
+
val intent = Intent(this@MainActivity, OverlayService::class.java)
|
| 45 |
+
intent.action = OverlayService.ACTION_SHOW_SENSOR_ALERT
|
| 46 |
+
intent.putExtra("appName", sensor.appName)
|
| 47 |
+
intent.putExtra("sensor", sensor.sensor)
|
| 48 |
+
intent.putExtra("packageName", sensor.packageName)
|
| 49 |
+
startService(intent)
|
| 50 |
+
}
|
| 51 |
+
}
|
| 52 |
+
}
|
| 53 |
+
sensorMonitorHandler.postDelayed(this, 60000L)
|
| 54 |
+
}
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
private val clearDedupHandler = Handler(Looper.getMainLooper())
|
| 58 |
+
private val clearDedupRunnable = object : Runnable {
|
| 59 |
+
override fun run() {
|
| 60 |
+
alreadyAlerted.clear()
|
| 61 |
+
clearDedupHandler.postDelayed(this, 600_000L)
|
| 62 |
+
}
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
| 66 |
+
super.configureFlutterEngine(flutterEngine)
|
| 67 |
+
|
| 68 |
+
// ── Accessibility Channel ────────────────────────────────────
|
| 69 |
+
accessibilityChannel = MethodChannel(
|
| 70 |
+
flutterEngine.dartExecutor.binaryMessenger,
|
| 71 |
+
"in.inmodel.premithius/accessibility"
|
| 72 |
+
).also { channel ->
|
| 73 |
+
channel.setMethodCallHandler { call, result ->
|
| 74 |
+
when (call.method) {
|
| 75 |
+
"isServiceEnabled" -> {
|
| 76 |
+
val am = getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
|
| 77 |
+
val enabled = am.getEnabledAccessibilityServiceList(
|
| 78 |
+
AccessibilityServiceInfo.FEEDBACK_ALL_MASK
|
| 79 |
+
).any {
|
| 80 |
+
it.resolveInfo.serviceInfo.packageName == packageName &&
|
| 81 |
+
it.resolveInfo.serviceInfo.name.contains("PremithiusAccessibilityService")
|
| 82 |
+
}
|
| 83 |
+
result.success(enabled)
|
| 84 |
+
}
|
| 85 |
+
"launchApp" -> {
|
| 86 |
+
val pkg = call.argument<String>("packageName")
|
| 87 |
+
if (pkg != null) {
|
| 88 |
+
val intent = packageManager.getLaunchIntentForPackage(pkg)
|
| 89 |
+
if (intent != null) startActivity(intent)
|
| 90 |
+
}
|
| 91 |
+
result.success(null)
|
| 92 |
+
}
|
| 93 |
+
else -> result.notImplemented()
|
| 94 |
+
}
|
| 95 |
+
}
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
// ── Auth Channel ─────────────────────────────────────────────
|
| 99 |
+
authChannel = MethodChannel(
|
| 100 |
+
flutterEngine.dartExecutor.binaryMessenger,
|
| 101 |
+
"in.inmodel.premithius/auth"
|
| 102 |
+
)
|
| 103 |
+
// Dart side sets the handler; Kotlin side may invoke methods on it
|
| 104 |
+
|
| 105 |
+
// ── Installed Apps Channel ───────────────────────────────────
|
| 106 |
+
MethodChannel(
|
| 107 |
+
flutterEngine.dartExecutor.binaryMessenger,
|
| 108 |
+
"in.inmodel.premithius/apps"
|
| 109 |
+
).setMethodCallHandler { call, result ->
|
| 110 |
+
if (call.method == "getInstalledApps") {
|
| 111 |
+
val pm = packageManager
|
| 112 |
+
val apps = pm.getInstalledApplications(PackageManager.GET_META_DATA)
|
| 113 |
+
.filter { app ->
|
| 114 |
+
// Only user-installed apps OR system apps that have a launcher intent
|
| 115 |
+
(app.flags and ApplicationInfo.FLAG_SYSTEM) == 0 ||
|
| 116 |
+
pm.getLaunchIntentForPackage(app.packageName) != null
|
| 117 |
+
}
|
| 118 |
+
.filter { it.packageName != "in.inmodel.premithus" }
|
| 119 |
+
.map { app ->
|
| 120 |
+
mapOf(
|
| 121 |
+
"packageName" to app.packageName,
|
| 122 |
+
"appName" to pm.getApplicationLabel(app).toString()
|
| 123 |
+
)
|
| 124 |
+
}
|
| 125 |
+
result.success(apps)
|
| 126 |
+
} else {
|
| 127 |
+
result.notImplemented()
|
| 128 |
+
}
|
| 129 |
+
}
|
| 130 |
+
// ── VPN Channel ──────────────────────────────────────────────
|
| 131 |
+
MethodChannel(
|
| 132 |
+
flutterEngine.dartExecutor.binaryMessenger,
|
| 133 |
+
"in.inmodel.premithius/vpn"
|
| 134 |
+
).setMethodCallHandler { call, result ->
|
| 135 |
+
when (call.method) {
|
| 136 |
+
"startVpn" -> {
|
| 137 |
+
val vpnIntent = VpnService.prepare(this)
|
| 138 |
+
if (vpnIntent != null) {
|
| 139 |
+
startActivityForResult(vpnIntent, VPN_REQUEST_CODE)
|
| 140 |
+
pendingVpnResult = result
|
| 141 |
+
} else {
|
| 142 |
+
val intent = Intent(this, KavachaVpnService::class.java)
|
| 143 |
+
intent.action = KavachaVpnService.ACTION_START
|
| 144 |
+
startService(intent)
|
| 145 |
+
result.success("started")
|
| 146 |
+
}
|
| 147 |
+
}
|
| 148 |
+
"stopVpn" -> {
|
| 149 |
+
val intent = Intent(this, KavachaVpnService::class.java)
|
| 150 |
+
intent.action = KavachaVpnService.ACTION_STOP
|
| 151 |
+
startService(intent)
|
| 152 |
+
result.success("stopped")
|
| 153 |
+
}
|
| 154 |
+
"getLastEvent" -> {
|
| 155 |
+
val prefs = getSharedPreferences("vpn_events", Context.MODE_PRIVATE)
|
| 156 |
+
result.success(prefs.getString("last_event", null))
|
| 157 |
+
}
|
| 158 |
+
"reloadBlocklist" -> {
|
| 159 |
+
val intent = Intent(this, KavachaVpnService::class.java)
|
| 160 |
+
intent.action = "ACTION_RELOAD_BLOCKLIST"
|
| 161 |
+
startService(intent)
|
| 162 |
+
result.success(null)
|
| 163 |
+
}
|
| 164 |
+
else -> result.notImplemented()
|
| 165 |
+
}
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
// ── Telemetry Channel ─────────────────────────────────────────
|
| 169 |
+
MethodChannel(
|
| 170 |
+
flutterEngine.dartExecutor.binaryMessenger,
|
| 171 |
+
"in.inmodel.premithius/telemetry"
|
| 172 |
+
).setMethodCallHandler { call, result ->
|
| 173 |
+
val helper = TelemetryHelper(this@MainActivity)
|
| 174 |
+
when (call.method) {
|
| 175 |
+
"hasUsagePermission" -> result.success(helper.hasUsageStatsPermission())
|
| 176 |
+
"getPrivacyStories" -> {
|
| 177 |
+
thread {
|
| 178 |
+
val sensors = try { helper.getActiveSensors() } catch(e:Exception){ emptyList() }
|
| 179 |
+
val perms = try { helper.getPermissionStats() } catch(e:Exception){ emptyList() }
|
| 180 |
+
val data = try { helper.getSleepTimeDataUsage() } catch(e:Exception){ emptyList() }
|
| 181 |
+
val stories = helper.generatePrivacyStories(sensors, perms, data)
|
| 182 |
+
val jsonArray = JSONArray()
|
| 183 |
+
for (s in stories) {
|
| 184 |
+
val obj = JSONObject()
|
| 185 |
+
obj.put("severity", s.severity)
|
| 186 |
+
obj.put("emoji", s.emoji)
|
| 187 |
+
obj.put("english", s.english)
|
| 188 |
+
obj.put("hindi", s.hindi)
|
| 189 |
+
obj.put("packageName", s.packageName)
|
| 190 |
+
jsonArray.put(obj)
|
| 191 |
+
}
|
| 192 |
+
Handler(Looper.getMainLooper()).post { result.success(jsonArray.toString()) }
|
| 193 |
+
}
|
| 194 |
+
}
|
| 195 |
+
"getActiveSensors" -> {
|
| 196 |
+
thread {
|
| 197 |
+
val sensors = try { helper.getActiveSensors() } catch(e:Exception){ emptyList() }
|
| 198 |
+
val jsonArray = JSONArray()
|
| 199 |
+
for (s in sensors) {
|
| 200 |
+
val obj = JSONObject()
|
| 201 |
+
obj.put("packageName", s.packageName)
|
| 202 |
+
obj.put("appName", s.appName)
|
| 203 |
+
obj.put("sensor", s.sensor)
|
| 204 |
+
obj.put("isActive", s.isActive)
|
| 205 |
+
jsonArray.put(obj)
|
| 206 |
+
}
|
| 207 |
+
Handler(Looper.getMainLooper()).post { result.success(jsonArray.toString()) }
|
| 208 |
+
}
|
| 209 |
+
}
|
| 210 |
+
"getDataUsage" -> {
|
| 211 |
+
thread {
|
| 212 |
+
val data = try { helper.getSleepTimeDataUsage() } catch(e:Exception){ emptyList() }
|
| 213 |
+
val jsonArray = JSONArray()
|
| 214 |
+
for (d in data) {
|
| 215 |
+
val obj = JSONObject()
|
| 216 |
+
obj.put("packageName", d.packageName)
|
| 217 |
+
obj.put("appName", d.appName)
|
| 218 |
+
obj.put("bytesSent", d.bytesSent)
|
| 219 |
+
obj.put("mbSent", d.mbSent)
|
| 220 |
+
jsonArray.put(obj)
|
| 221 |
+
}
|
| 222 |
+
Handler(Looper.getMainLooper()).post { result.success(jsonArray.toString()) }
|
| 223 |
+
}
|
| 224 |
+
}
|
| 225 |
+
"getPermissionStats" -> {
|
| 226 |
+
thread {
|
| 227 |
+
val perms = try { helper.getPermissionStats() } catch(e:Exception){ emptyList() }
|
| 228 |
+
val jsonArray = JSONArray()
|
| 229 |
+
for (p in perms) {
|
| 230 |
+
val obj = JSONObject()
|
| 231 |
+
obj.put("packageName", p.packageName)
|
| 232 |
+
obj.put("appName", p.appName)
|
| 233 |
+
val counts = JSONObject()
|
| 234 |
+
for ((k, v) in p.accessCounts) { counts.put(k, v) }
|
| 235 |
+
obj.put("accessCounts", counts)
|
| 236 |
+
obj.put("isSystemApp", p.isSystemApp)
|
| 237 |
+
jsonArray.put(obj)
|
| 238 |
+
}
|
| 239 |
+
Handler(Looper.getMainLooper()).post { result.success(jsonArray.toString()) }
|
| 240 |
+
}
|
| 241 |
+
}
|
| 242 |
+
else -> result.notImplemented()
|
| 243 |
+
}
|
| 244 |
+
}
|
| 245 |
+
}
|
| 246 |
+
|
| 247 |
+
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
| 248 |
+
if (requestCode == VPN_REQUEST_CODE) {
|
| 249 |
+
if (resultCode == RESULT_OK) {
|
| 250 |
+
val intent = Intent(this, KavachaVpnService::class.java)
|
| 251 |
+
intent.action = KavachaVpnService.ACTION_START
|
| 252 |
+
startService(intent)
|
| 253 |
+
pendingVpnResult?.success("started")
|
| 254 |
+
} else {
|
| 255 |
+
pendingVpnResult?.success("denied")
|
| 256 |
+
}
|
| 257 |
+
pendingVpnResult = null
|
| 258 |
+
}
|
| 259 |
+
super.onActivityResult(requestCode, resultCode, data)
|
| 260 |
+
}
|
| 261 |
+
|
| 262 |
+
override fun onCreate(savedInstanceState: Bundle?) {
|
| 263 |
+
super.onCreate(savedInstanceState)
|
| 264 |
+
sensorMonitorHandler.post(sensorMonitorRunnable)
|
| 265 |
+
clearDedupHandler.postDelayed(clearDedupRunnable, 600000L)
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
override fun onDestroy() {
|
| 269 |
+
super.onDestroy()
|
| 270 |
+
sensorMonitorHandler.removeCallbacksAndMessages(null)
|
| 271 |
+
clearDedupHandler.removeCallbacksAndMessages(null)
|
| 272 |
+
}
|
| 273 |
+
}
|
android/app/src/main/kotlin/in/inmodel/premithus/OverlayService.kt
ADDED
|
@@ -0,0 +1,515 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package `in`.inmodel.premithus
|
| 2 |
+
|
| 3 |
+
import android.app.Service
|
| 4 |
+
import android.content.Context
|
| 5 |
+
import android.content.Intent
|
| 6 |
+
import android.graphics.Color
|
| 7 |
+
import android.graphics.PixelFormat
|
| 8 |
+
import android.os.Handler
|
| 9 |
+
import android.os.IBinder
|
| 10 |
+
import android.os.Looper
|
| 11 |
+
import android.view.Gravity
|
| 12 |
+
import android.view.ViewGroup
|
| 13 |
+
import android.view.WindowManager
|
| 14 |
+
import android.widget.Button
|
| 15 |
+
import android.widget.LinearLayout
|
| 16 |
+
import android.widget.TextView
|
| 17 |
+
import io.flutter.FlutterInjector
|
| 18 |
+
import io.flutter.embedding.engine.FlutterEngine
|
| 19 |
+
import io.flutter.embedding.engine.dart.DartExecutor
|
| 20 |
+
import io.flutter.plugin.common.MethodChannel
|
| 21 |
+
|
| 22 |
+
class OverlayService : Service() {
|
| 23 |
+
|
| 24 |
+
companion object {
|
| 25 |
+
const val ACTION_SHOW_AUTH = "ACTION_SHOW_AUTH"
|
| 26 |
+
const val ACTION_SHOW_DOMAIN_BLOCK = "ACTION_SHOW_DOMAIN_BLOCK"
|
| 27 |
+
const val ACTION_SHOW_SENSOR_ALERT = "ACTION_SHOW_SENSOR_ALERT"
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
private lateinit var windowManager: WindowManager
|
| 31 |
+
private var overlayView: LinearLayout? = null
|
| 32 |
+
private var authOverlayView: LinearLayout? = null
|
| 33 |
+
private var autoDismissHandler: Handler? = null
|
| 34 |
+
|
| 35 |
+
// Background single engine for scam analysis
|
| 36 |
+
private lateinit var flutterEngine: FlutterEngine
|
| 37 |
+
private lateinit var methodChannel: MethodChannel
|
| 38 |
+
private val CHANNEL = "in.inmodel.premithus/scam"
|
| 39 |
+
|
| 40 |
+
override fun onCreate() {
|
| 41 |
+
super.onCreate()
|
| 42 |
+
windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
| 43 |
+
|
| 44 |
+
flutterEngine = FlutterEngine(this)
|
| 45 |
+
flutterEngine.dartExecutor.executeDartEntrypoint(
|
| 46 |
+
DartExecutor.DartEntrypoint(
|
| 47 |
+
FlutterInjector.instance().flutterLoader().findAppBundlePath(),
|
| 48 |
+
"scamMain"
|
| 49 |
+
)
|
| 50 |
+
)
|
| 51 |
+
methodChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
| 55 |
+
if (intent?.action == "in.inmodel.premithus.ACTION_ANALYZE_SMS") {
|
| 56 |
+
val sender = intent.getStringExtra("sender") ?: ""
|
| 57 |
+
val messageBody = intent.getStringExtra("messageBody") ?: ""
|
| 58 |
+
|
| 59 |
+
val params = mapOf("sender" to sender, "messageBody" to messageBody)
|
| 60 |
+
|
| 61 |
+
methodChannel.invokeMethod("analyze", params, object : MethodChannel.Result {
|
| 62 |
+
override fun success(result: Any?) {
|
| 63 |
+
val map = result as? Map<String, Any> ?: return
|
| 64 |
+
val isScam = map["isScam"] as? Boolean ?: false
|
| 65 |
+
val category = map["category"] as? String ?: "Unknown"
|
| 66 |
+
val layerLabel = map["layerLabel"] as? String ?: ""
|
| 67 |
+
if (isScam) {
|
| 68 |
+
showOverlay(sender, messageBody, category, layerLabel)
|
| 69 |
+
}
|
| 70 |
+
}
|
| 71 |
+
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {}
|
| 72 |
+
override fun notImplemented() {}
|
| 73 |
+
})
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
if (intent?.action == ACTION_SHOW_AUTH) {
|
| 77 |
+
val packageName = intent.getStringExtra("package_name") ?: return START_STICKY
|
| 78 |
+
val appName = intent.getStringExtra("app_name") ?: packageName
|
| 79 |
+
showAuthOverlay(packageName, appName)
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
if (intent?.action == ACTION_SHOW_DOMAIN_BLOCK) {
|
| 83 |
+
val domain = intent.getStringExtra("domain") ?: ""
|
| 84 |
+
val category = intent.getStringExtra("category") ?: ""
|
| 85 |
+
showDomainBlockOverlay(domain, category)
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
if (intent?.action == ACTION_SHOW_SENSOR_ALERT) {
|
| 89 |
+
val appName = intent.getStringExtra("appName") ?: ""
|
| 90 |
+
val sensor = intent.getStringExtra("sensor") ?: ""
|
| 91 |
+
val packageName = intent.getStringExtra("packageName") ?: ""
|
| 92 |
+
showSensorOverlay(appName, sensor, packageName)
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
return START_STICKY
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
private fun showAuthOverlay(packageName: String, appName: String) {
|
| 99 |
+
// Dismiss any existing auth overlay
|
| 100 |
+
authOverlayView?.let {
|
| 101 |
+
try { windowManager.removeView(it) } catch (e: Exception) {}
|
| 102 |
+
}
|
| 103 |
+
autoDismissHandler?.removeCallbacksAndMessages(null)
|
| 104 |
+
|
| 105 |
+
authOverlayView = LinearLayout(this).apply {
|
| 106 |
+
orientation = LinearLayout.VERTICAL
|
| 107 |
+
setBackgroundColor(android.graphics.Color.parseColor("#1A1A2E"))
|
| 108 |
+
setPadding(40, 48, 40, 48)
|
| 109 |
+
|
| 110 |
+
val titleView = android.widget.TextView(context).apply {
|
| 111 |
+
text = "\uD83D\uDD12 App Locked — $appName"
|
| 112 |
+
setTextColor(android.graphics.Color.WHITE)
|
| 113 |
+
textSize = 18f
|
| 114 |
+
setTypeface(null, android.graphics.Typeface.BOLD)
|
| 115 |
+
}
|
| 116 |
+
addView(titleView)
|
| 117 |
+
|
| 118 |
+
val subtitleView = android.widget.TextView(context).apply {
|
| 119 |
+
text = "Authenticate to open"
|
| 120 |
+
setTextColor(android.graphics.Color.parseColor("#AAAACC"))
|
| 121 |
+
textSize = 14f
|
| 122 |
+
setPadding(0, 8, 0, 24)
|
| 123 |
+
}
|
| 124 |
+
addView(subtitleView)
|
| 125 |
+
|
| 126 |
+
val buttonLayout = LinearLayout(context).apply {
|
| 127 |
+
orientation = LinearLayout.HORIZONTAL
|
| 128 |
+
|
| 129 |
+
val biometricBtn = android.widget.Button(context).apply {
|
| 130 |
+
text = "Use Biometrics"
|
| 131 |
+
setTextColor(android.graphics.Color.WHITE)
|
| 132 |
+
setBackgroundColor(android.graphics.Color.parseColor("#6C63FF"))
|
| 133 |
+
layoutParams = LinearLayout.LayoutParams(
|
| 134 |
+
0, android.view.ViewGroup.LayoutParams.WRAP_CONTENT, 1f
|
| 135 |
+
).apply { setMargins(0, 0, 8, 0) }
|
| 136 |
+
setOnClickListener {
|
| 137 |
+
// Send biometric auth request to Flutter via MainActivity channel
|
| 138 |
+
Handler(Looper.getMainLooper()).post {
|
| 139 |
+
val args = mapOf("packageName" to packageName, "appName" to appName)
|
| 140 |
+
MainActivity.authChannel?.invokeMethod("requestBiometricAuth", args)
|
| 141 |
+
}
|
| 142 |
+
dismissAuthOverlay()
|
| 143 |
+
}
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
val cancelBtn = android.widget.Button(context).apply {
|
| 147 |
+
text = "Cancel"
|
| 148 |
+
setTextColor(android.graphics.Color.WHITE)
|
| 149 |
+
setBackgroundColor(android.graphics.Color.parseColor("#444466"))
|
| 150 |
+
layoutParams = LinearLayout.LayoutParams(
|
| 151 |
+
0, android.view.ViewGroup.LayoutParams.WRAP_CONTENT, 1f
|
| 152 |
+
).apply { setMargins(8, 0, 0, 0) }
|
| 153 |
+
setOnClickListener { dismissAuthOverlay() }
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
addView(biometricBtn)
|
| 157 |
+
addView(cancelBtn)
|
| 158 |
+
}
|
| 159 |
+
addView(buttonLayout)
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
val params = android.view.WindowManager.LayoutParams(
|
| 163 |
+
android.view.WindowManager.LayoutParams.MATCH_PARENT,
|
| 164 |
+
android.view.WindowManager.LayoutParams.WRAP_CONTENT,
|
| 165 |
+
android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
|
| 166 |
+
android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
|
| 167 |
+
android.graphics.PixelFormat.TRANSLUCENT
|
| 168 |
+
).apply {
|
| 169 |
+
gravity = android.view.Gravity.TOP
|
| 170 |
+
y = 100
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
windowManager.addView(authOverlayView, params)
|
| 174 |
+
|
| 175 |
+
// Auto dismiss after 15 seconds
|
| 176 |
+
autoDismissHandler = Handler(Looper.getMainLooper())
|
| 177 |
+
autoDismissHandler!!.postDelayed({ dismissAuthOverlay() }, 15_000)
|
| 178 |
+
}
|
| 179 |
+
|
| 180 |
+
private fun dismissAuthOverlay() {
|
| 181 |
+
autoDismissHandler?.removeCallbacksAndMessages(null)
|
| 182 |
+
authOverlayView?.let {
|
| 183 |
+
try { windowManager.removeView(it) } catch (e: Exception) {}
|
| 184 |
+
authOverlayView = null
|
| 185 |
+
}
|
| 186 |
+
}
|
| 187 |
+
|
| 188 |
+
private fun showOverlay(sender: String, message: String, category: String, layerLabel: String = "") {
|
| 189 |
+
if (overlayView != null) {
|
| 190 |
+
windowManager.removeView(overlayView)
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
overlayView = LinearLayout(this).apply {
|
| 194 |
+
orientation = LinearLayout.VERTICAL
|
| 195 |
+
setBackgroundColor(Color.parseColor("#E53935")) // Red background
|
| 196 |
+
setPadding(32, 32, 32, 32)
|
| 197 |
+
|
| 198 |
+
val titleView = TextView(context).apply {
|
| 199 |
+
text = "HIGH RISK — $category"
|
| 200 |
+
setTextColor(Color.WHITE)
|
| 201 |
+
textSize = 18f
|
| 202 |
+
setTypeface(null, android.graphics.Typeface.BOLD)
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
val previewView = TextView(context).apply {
|
| 206 |
+
text = "$sender: ${message.take(60)}..."
|
| 207 |
+
setTextColor(Color.WHITE)
|
| 208 |
+
textSize = 14f
|
| 209 |
+
setPadding(0, 8, 0, 0)
|
| 210 |
+
}
|
| 211 |
+
|
| 212 |
+
addView(titleView)
|
| 213 |
+
|
| 214 |
+
if (layerLabel.isNotEmpty()) {
|
| 215 |
+
val caughtByView = TextView(context).apply {
|
| 216 |
+
text = "\uD83D\uDEE1\uFE0F Caught by: $layerLabel"
|
| 217 |
+
setTextColor(Color.parseColor("#FFCDD2"))
|
| 218 |
+
textSize = 12f
|
| 219 |
+
setPadding(0, 4, 0, 0)
|
| 220 |
+
}
|
| 221 |
+
addView(caughtByView)
|
| 222 |
+
}
|
| 223 |
+
addView(previewView)
|
| 224 |
+
|
| 225 |
+
val buttonLayout = LinearLayout(context).apply {
|
| 226 |
+
orientation = LinearLayout.HORIZONTAL
|
| 227 |
+
setPadding(0, 32, 0, 0)
|
| 228 |
+
|
| 229 |
+
val confirmBtn = Button(context).apply {
|
| 230 |
+
text = "Confirm Scam"
|
| 231 |
+
setTextColor(Color.WHITE)
|
| 232 |
+
setBackgroundColor(Color.parseColor("#B71C1C"))
|
| 233 |
+
layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f).apply {
|
| 234 |
+
setMargins(0, 0, 8, 0)
|
| 235 |
+
}
|
| 236 |
+
setOnClickListener {
|
| 237 |
+
val params = mapOf("sender" to sender, "messageBody" to message, "isScam" to true)
|
| 238 |
+
methodChannel.invokeMethod("feedback", params)
|
| 239 |
+
windowManager.removeView(overlayView)
|
| 240 |
+
overlayView = null
|
| 241 |
+
}
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
val safeBtn = Button(context).apply {
|
| 245 |
+
text = "Not a Scam"
|
| 246 |
+
setTextColor(Color.WHITE)
|
| 247 |
+
setBackgroundColor(Color.parseColor("#43A047"))
|
| 248 |
+
layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f).apply {
|
| 249 |
+
setMargins(8, 0, 0, 0)
|
| 250 |
+
}
|
| 251 |
+
setOnClickListener {
|
| 252 |
+
val params = mapOf("sender" to sender, "messageBody" to message, "isScam" to false)
|
| 253 |
+
methodChannel.invokeMethod("feedback", params)
|
| 254 |
+
windowManager.removeView(overlayView)
|
| 255 |
+
overlayView = null
|
| 256 |
+
}
|
| 257 |
+
}
|
| 258 |
+
|
| 259 |
+
addView(confirmBtn)
|
| 260 |
+
addView(safeBtn)
|
| 261 |
+
}
|
| 262 |
+
addView(buttonLayout)
|
| 263 |
+
|
| 264 |
+
setOnClickListener {
|
| 265 |
+
windowManager.removeView(overlayView)
|
| 266 |
+
overlayView = null
|
| 267 |
+
}
|
| 268 |
+
}
|
| 269 |
+
|
| 270 |
+
val params = WindowManager.LayoutParams(
|
| 271 |
+
WindowManager.LayoutParams.MATCH_PARENT,
|
| 272 |
+
WindowManager.LayoutParams.WRAP_CONTENT,
|
| 273 |
+
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
|
| 274 |
+
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
|
| 275 |
+
PixelFormat.TRANSLUCENT
|
| 276 |
+
).apply {
|
| 277 |
+
gravity = Gravity.TOP
|
| 278 |
+
y = 50 // small inset
|
| 279 |
+
}
|
| 280 |
+
|
| 281 |
+
windowManager.addView(overlayView, params)
|
| 282 |
+
|
| 283 |
+
// Auto dismiss after 8 seconds
|
| 284 |
+
Handler(Looper.getMainLooper()).postDelayed({
|
| 285 |
+
if (overlayView != null) {
|
| 286 |
+
windowManager.removeView(overlayView)
|
| 287 |
+
overlayView = null
|
| 288 |
+
}
|
| 289 |
+
}, 8000)
|
| 290 |
+
|
| 291 |
+
// Force widget refresh whenever a scam SMS is detected
|
| 292 |
+
try {
|
| 293 |
+
PremithiusWidget.forceRefresh(applicationContext)
|
| 294 |
+
} catch(e: Exception) {}
|
| 295 |
+
}
|
| 296 |
+
|
| 297 |
+
private fun showDomainBlockOverlay(domain: String, category: String) {
|
| 298 |
+
if (overlayView != null) {
|
| 299 |
+
windowManager.removeView(overlayView)
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
overlayView = LinearLayout(this).apply {
|
| 303 |
+
orientation = LinearLayout.VERTICAL
|
| 304 |
+
setBackgroundColor(Color.parseColor("#E53935")) // Red background
|
| 305 |
+
setPadding(32, 32, 32, 32)
|
| 306 |
+
|
| 307 |
+
val titleView = TextView(context).apply {
|
| 308 |
+
text = "Scam Domain Blocked"
|
| 309 |
+
setTextColor(Color.WHITE)
|
| 310 |
+
textSize = 18f
|
| 311 |
+
setTypeface(null, android.graphics.Typeface.BOLD)
|
| 312 |
+
}
|
| 313 |
+
|
| 314 |
+
val caughtByView = TextView(context).apply {
|
| 315 |
+
text = "\uD83D\uDEE1\uFE0F Caught by: Kavacha VPN Shield"
|
| 316 |
+
setTextColor(Color.parseColor("#FFCDD2"))
|
| 317 |
+
textSize = 12f
|
| 318 |
+
setPadding(0, 4, 0, 8)
|
| 319 |
+
}
|
| 320 |
+
|
| 321 |
+
val subtitleView = TextView(context).apply {
|
| 322 |
+
text = domain
|
| 323 |
+
setTextColor(Color.WHITE)
|
| 324 |
+
textSize = 16f
|
| 325 |
+
setTypeface(null, android.graphics.Typeface.BOLD)
|
| 326 |
+
}
|
| 327 |
+
|
| 328 |
+
val detailView = TextView(context).apply {
|
| 329 |
+
text = category
|
| 330 |
+
setTextColor(Color.parseColor("#FFCDD2"))
|
| 331 |
+
textSize = 14f
|
| 332 |
+
setPadding(0, 4, 0, 0)
|
| 333 |
+
}
|
| 334 |
+
|
| 335 |
+
addView(titleView)
|
| 336 |
+
addView(caughtByView)
|
| 337 |
+
addView(subtitleView)
|
| 338 |
+
addView(detailView)
|
| 339 |
+
|
| 340 |
+
val buttonLayout = LinearLayout(context).apply {
|
| 341 |
+
orientation = LinearLayout.HORIZONTAL
|
| 342 |
+
setPadding(0, 32, 0, 0)
|
| 343 |
+
|
| 344 |
+
val detailsBtn = Button(context).apply {
|
| 345 |
+
text = "View Details"
|
| 346 |
+
setTextColor(Color.WHITE)
|
| 347 |
+
setBackgroundColor(Color.parseColor("#B71C1C"))
|
| 348 |
+
layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f).apply {
|
| 349 |
+
setMargins(0, 0, 8, 0)
|
| 350 |
+
}
|
| 351 |
+
setOnClickListener {
|
| 352 |
+
val launchIntent = packageManager.getLaunchIntentForPackage(packageName)
|
| 353 |
+
if (launchIntent != null) {
|
| 354 |
+
launchIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
| 355 |
+
startActivity(launchIntent)
|
| 356 |
+
}
|
| 357 |
+
windowManager.removeView(overlayView)
|
| 358 |
+
overlayView = null
|
| 359 |
+
}
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
+
val dismissBtn = Button(context).apply {
|
| 363 |
+
text = "Dismiss"
|
| 364 |
+
setTextColor(Color.WHITE)
|
| 365 |
+
setBackgroundColor(Color.parseColor("#444466"))
|
| 366 |
+
layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f).apply {
|
| 367 |
+
setMargins(8, 0, 0, 0)
|
| 368 |
+
}
|
| 369 |
+
setOnClickListener {
|
| 370 |
+
windowManager.removeView(overlayView)
|
| 371 |
+
overlayView = null
|
| 372 |
+
}
|
| 373 |
+
}
|
| 374 |
+
|
| 375 |
+
addView(detailsBtn)
|
| 376 |
+
addView(dismissBtn)
|
| 377 |
+
}
|
| 378 |
+
addView(buttonLayout)
|
| 379 |
+
}
|
| 380 |
+
|
| 381 |
+
val params = WindowManager.LayoutParams(
|
| 382 |
+
WindowManager.LayoutParams.MATCH_PARENT,
|
| 383 |
+
WindowManager.LayoutParams.WRAP_CONTENT,
|
| 384 |
+
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
|
| 385 |
+
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
|
| 386 |
+
PixelFormat.TRANSLUCENT
|
| 387 |
+
).apply {
|
| 388 |
+
gravity = Gravity.TOP
|
| 389 |
+
y = 50 // small inset
|
| 390 |
+
}
|
| 391 |
+
|
| 392 |
+
windowManager.addView(overlayView, params)
|
| 393 |
+
|
| 394 |
+
// Auto dismiss after 6 seconds
|
| 395 |
+
Handler(Looper.getMainLooper()).postDelayed({
|
| 396 |
+
if (overlayView != null) {
|
| 397 |
+
windowManager.removeView(overlayView)
|
| 398 |
+
overlayView = null
|
| 399 |
+
}
|
| 400 |
+
}, 6000)
|
| 401 |
+
|
| 402 |
+
// Force widget refresh whenever a domain is blocked
|
| 403 |
+
try {
|
| 404 |
+
PremithiusWidget.forceRefresh(applicationContext)
|
| 405 |
+
} catch(e: Exception) {}
|
| 406 |
+
}
|
| 407 |
+
|
| 408 |
+
private fun showSensorOverlay(appName: String, sensor: String, packageName: String) {
|
| 409 |
+
if (overlayView != null) {
|
| 410 |
+
windowManager.removeView(overlayView)
|
| 411 |
+
}
|
| 412 |
+
|
| 413 |
+
overlayView = LinearLayout(this).apply {
|
| 414 |
+
orientation = LinearLayout.VERTICAL
|
| 415 |
+
setBackgroundColor(Color.parseColor("#FFCC7700")) // Orange background for sensor alert
|
| 416 |
+
setPadding(32, 32, 32, 32)
|
| 417 |
+
|
| 418 |
+
val titleView = TextView(context).apply {
|
| 419 |
+
text = "${sensor.capitalize()} active in background"
|
| 420 |
+
setTextColor(Color.WHITE)
|
| 421 |
+
textSize = 18f
|
| 422 |
+
setTypeface(null, android.graphics.Typeface.BOLD)
|
| 423 |
+
}
|
| 424 |
+
|
| 425 |
+
val caughtByView = TextView(context).apply {
|
| 426 |
+
text = "\uD83D\uDEE1\uFE0F Caught by: Data Shield"
|
| 427 |
+
setTextColor(Color.parseColor("#FFE0B2"))
|
| 428 |
+
textSize = 12f
|
| 429 |
+
setPadding(0, 4, 0, 8)
|
| 430 |
+
}
|
| 431 |
+
|
| 432 |
+
val subtitleView = TextView(context).apply {
|
| 433 |
+
text = "$appName is using your $sensor right now"
|
| 434 |
+
setTextColor(Color.WHITE)
|
| 435 |
+
textSize = 16f
|
| 436 |
+
}
|
| 437 |
+
|
| 438 |
+
addView(titleView)
|
| 439 |
+
addView(caughtByView)
|
| 440 |
+
addView(subtitleView)
|
| 441 |
+
|
| 442 |
+
val buttonLayout = LinearLayout(context).apply {
|
| 443 |
+
orientation = LinearLayout.HORIZONTAL
|
| 444 |
+
setPadding(0, 32, 0, 0)
|
| 445 |
+
|
| 446 |
+
val detailsBtn = Button(context).apply {
|
| 447 |
+
text = "Open Data Shield"
|
| 448 |
+
setTextColor(Color.WHITE)
|
| 449 |
+
setBackgroundColor(Color.parseColor("#E65100"))
|
| 450 |
+
layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f).apply {
|
| 451 |
+
setMargins(0, 0, 8, 0)
|
| 452 |
+
}
|
| 453 |
+
setOnClickListener {
|
| 454 |
+
val launchIntent = packageManager.getLaunchIntentForPackage(context.packageName)
|
| 455 |
+
if (launchIntent != null) {
|
| 456 |
+
launchIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
|
| 457 |
+
startActivity(launchIntent)
|
| 458 |
+
}
|
| 459 |
+
windowManager.removeView(overlayView)
|
| 460 |
+
overlayView = null
|
| 461 |
+
}
|
| 462 |
+
}
|
| 463 |
+
|
| 464 |
+
val dismissBtn = Button(context).apply {
|
| 465 |
+
text = "Dismiss"
|
| 466 |
+
setTextColor(Color.WHITE)
|
| 467 |
+
setBackgroundColor(Color.parseColor("#444466"))
|
| 468 |
+
layoutParams = LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.WRAP_CONTENT, 1f).apply {
|
| 469 |
+
setMargins(8, 0, 0, 0)
|
| 470 |
+
}
|
| 471 |
+
setOnClickListener {
|
| 472 |
+
windowManager.removeView(overlayView)
|
| 473 |
+
overlayView = null
|
| 474 |
+
}
|
| 475 |
+
}
|
| 476 |
+
|
| 477 |
+
addView(detailsBtn)
|
| 478 |
+
addView(dismissBtn)
|
| 479 |
+
}
|
| 480 |
+
addView(buttonLayout)
|
| 481 |
+
}
|
| 482 |
+
|
| 483 |
+
val params = WindowManager.LayoutParams(
|
| 484 |
+
WindowManager.LayoutParams.MATCH_PARENT,
|
| 485 |
+
WindowManager.LayoutParams.WRAP_CONTENT,
|
| 486 |
+
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
|
| 487 |
+
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
|
| 488 |
+
PixelFormat.TRANSLUCENT
|
| 489 |
+
).apply {
|
| 490 |
+
gravity = Gravity.TOP
|
| 491 |
+
y = 50 // small inset
|
| 492 |
+
}
|
| 493 |
+
|
| 494 |
+
windowManager.addView(overlayView, params)
|
| 495 |
+
|
| 496 |
+
// Auto dismiss after 8 seconds
|
| 497 |
+
Handler(Looper.getMainLooper()).postDelayed({
|
| 498 |
+
if (overlayView != null) {
|
| 499 |
+
windowManager.removeView(overlayView)
|
| 500 |
+
overlayView = null
|
| 501 |
+
}
|
| 502 |
+
}, 8000)
|
| 503 |
+
}
|
| 504 |
+
|
| 505 |
+
override fun onBind(intent: Intent?): IBinder? = null
|
| 506 |
+
|
| 507 |
+
override fun onDestroy() {
|
| 508 |
+
super.onDestroy()
|
| 509 |
+
flutterEngine.destroy()
|
| 510 |
+
if (overlayView != null) {
|
| 511 |
+
try { windowManager.removeView(overlayView) } catch (e: Exception) {}
|
| 512 |
+
}
|
| 513 |
+
dismissAuthOverlay()
|
| 514 |
+
}
|
| 515 |
+
}
|
android/app/src/main/kotlin/in/inmodel/premithus/PremithiusAccessibilityService.kt
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package `in`.inmodel.premithus
|
| 2 |
+
|
| 3 |
+
import android.accessibilityservice.AccessibilityService
|
| 4 |
+
import android.accessibilityservice.AccessibilityServiceInfo
|
| 5 |
+
import android.content.Intent
|
| 6 |
+
import android.os.Handler
|
| 7 |
+
import android.os.Looper
|
| 8 |
+
import android.util.Log
|
| 9 |
+
import android.view.accessibility.AccessibilityEvent
|
| 10 |
+
|
| 11 |
+
class PremithiusAccessibilityService : AccessibilityService() {
|
| 12 |
+
|
| 13 |
+
companion object {
|
| 14 |
+
// Shared state — readable from other Kotlin classes
|
| 15 |
+
var foregroundPackage: String = ""
|
| 16 |
+
var foregroundAppName: String = ""
|
| 17 |
+
var isServiceRunning: Boolean = false
|
| 18 |
+
|
| 19 |
+
// MethodChannel name for Flutter communication
|
| 20 |
+
const val CHANNEL = "in.inmodel.premithius/accessibility"
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
override fun onServiceConnected() {
|
| 24 |
+
isServiceRunning = true
|
| 25 |
+
// Configure which events to listen for
|
| 26 |
+
val info = AccessibilityServiceInfo()
|
| 27 |
+
info.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED or
|
| 28 |
+
AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
|
| 29 |
+
info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC
|
| 30 |
+
info.flags = AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS or
|
| 31 |
+
AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS
|
| 32 |
+
info.notificationTimeout = 100
|
| 33 |
+
serviceInfo = info
|
| 34 |
+
Log.d("PremithiusA11y", "Service connected")
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
override fun onAccessibilityEvent(event: AccessibilityEvent) {
|
| 38 |
+
if (event.eventType != AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) return
|
| 39 |
+
|
| 40 |
+
val packageName = event.packageName?.toString() ?: return
|
| 41 |
+
if (packageName == foregroundPackage) return // same app, ignore
|
| 42 |
+
if (packageName == "in.inmodel.premithus") return // ignore ourselves
|
| 43 |
+
|
| 44 |
+
foregroundPackage = packageName
|
| 45 |
+
foregroundAppName = getAppName(packageName)
|
| 46 |
+
|
| 47 |
+
Log.d("PremithiusA11y", "Foreground: $packageName")
|
| 48 |
+
|
| 49 |
+
// Feature 1: App Lock check
|
| 50 |
+
checkAppLock(packageName)
|
| 51 |
+
|
| 52 |
+
// Feature 2: Notify Flutter of foreground context
|
| 53 |
+
// (used by keyboard tone switching + OTP verification)
|
| 54 |
+
notifyFlutter(packageName, foregroundAppName)
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
override fun onInterrupt() {
|
| 58 |
+
isServiceRunning = false
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
// ── Feature 1: App Lock ──────────────────────────────────────
|
| 62 |
+
private fun checkAppLock(packageName: String) {
|
| 63 |
+
// Query locked_apps SQLite table via PremithiusDatabase
|
| 64 |
+
// Run on background thread — never block accessibility thread
|
| 65 |
+
Thread {
|
| 66 |
+
val isLocked = PremithiusDatabase.getInstance(this)
|
| 67 |
+
.lockedAppDao()
|
| 68 |
+
.isLocked(packageName)
|
| 69 |
+
|
| 70 |
+
if (isLocked) {
|
| 71 |
+
Log.d("PremithiusA11y", "LOCKED APP DETECTED: $packageName")
|
| 72 |
+
|
| 73 |
+
// Step 1: Immediately go home — app never shows
|
| 74 |
+
performGlobalAction(GLOBAL_ACTION_HOME)
|
| 75 |
+
|
| 76 |
+
// Step 2: Show auth overlay via OverlayService
|
| 77 |
+
val intent = Intent(this, OverlayService::class.java)
|
| 78 |
+
intent.action = OverlayService.ACTION_SHOW_AUTH
|
| 79 |
+
intent.putExtra("package_name", packageName)
|
| 80 |
+
intent.putExtra("app_name", foregroundAppName)
|
| 81 |
+
startService(intent)
|
| 82 |
+
|
| 83 |
+
// Step 3: Log access attempt
|
| 84 |
+
PremithiusDatabase.getInstance(this)
|
| 85 |
+
.lockedAppDao()
|
| 86 |
+
.logAccessAttempt(packageName, System.currentTimeMillis())
|
| 87 |
+
}
|
| 88 |
+
}.start()
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
// ── Feature 2: Notify Flutter ────────────────────────────────
|
| 92 |
+
private fun notifyFlutter(packageName: String, appName: String) {
|
| 93 |
+
// Send foreground context to Flutter via MethodChannel
|
| 94 |
+
val context = mapOf(
|
| 95 |
+
"package" to packageName,
|
| 96 |
+
"appName" to appName,
|
| 97 |
+
"toneContext" to getToneContext(packageName)
|
| 98 |
+
)
|
| 99 |
+
// Post to main thread for MethodChannel call
|
| 100 |
+
Handler(Looper.getMainLooper()).post {
|
| 101 |
+
MainActivity.accessibilityChannel?.invokeMethod("onForegroundChanged", context)
|
| 102 |
+
}
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
// ── Tone context mapping ─────────────────────────────────────
|
| 106 |
+
private fun getToneContext(packageName: String): String {
|
| 107 |
+
return when {
|
| 108 |
+
packageName.contains("whatsapp") ||
|
| 109 |
+
packageName.contains("telegram") ||
|
| 110 |
+
packageName.contains("signal") -> "casual"
|
| 111 |
+
|
| 112 |
+
packageName.contains("gmail") ||
|
| 113 |
+
packageName.contains("outlook") ||
|
| 114 |
+
packageName.contains("yahoo.mail") -> "formal"
|
| 115 |
+
|
| 116 |
+
packageName.contains("linkedin") -> "professional"
|
| 117 |
+
|
| 118 |
+
packageName.contains("twitter") ||
|
| 119 |
+
packageName.contains("instagram") -> "casual"
|
| 120 |
+
|
| 121 |
+
else -> "neutral"
|
| 122 |
+
}
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
// ── Helper: get human-readable app name ─────────────────────
|
| 126 |
+
private fun getAppName(packageName: String): String {
|
| 127 |
+
return try {
|
| 128 |
+
val pm = packageManager
|
| 129 |
+
val appInfo = pm.getApplicationInfo(packageName, 0)
|
| 130 |
+
pm.getApplicationLabel(appInfo).toString()
|
| 131 |
+
} catch (e: Exception) {
|
| 132 |
+
packageName
|
| 133 |
+
}
|
| 134 |
+
}
|
| 135 |
+
}
|
android/app/src/main/kotlin/in/inmodel/premithus/PremithiusDatabase.kt
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package `in`.inmodel.premithus
|
| 2 |
+
|
| 3 |
+
import android.content.Context
|
| 4 |
+
import android.database.sqlite.SQLiteDatabase
|
| 5 |
+
import android.database.sqlite.SQLiteOpenHelper
|
| 6 |
+
|
| 7 |
+
class PremithiusDatabase private constructor(context: Context)
|
| 8 |
+
: SQLiteOpenHelper(context, "premithius.db", null, 2) {
|
| 9 |
+
|
| 10 |
+
companion object {
|
| 11 |
+
@Volatile private var INSTANCE: PremithiusDatabase? = null
|
| 12 |
+
|
| 13 |
+
fun getInstance(context: Context): PremithiusDatabase {
|
| 14 |
+
return INSTANCE ?: synchronized(this) {
|
| 15 |
+
INSTANCE ?: PremithiusDatabase(context.applicationContext).also { INSTANCE = it }
|
| 16 |
+
}
|
| 17 |
+
}
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
override fun onCreate(db: SQLiteDatabase) {
|
| 21 |
+
// Tables created by Flutter (sqflite) — we just add new ones here
|
| 22 |
+
db.execSQL("""
|
| 23 |
+
CREATE TABLE IF NOT EXISTS locked_apps (
|
| 24 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 25 |
+
package_name TEXT UNIQUE NOT NULL,
|
| 26 |
+
app_name TEXT NOT NULL,
|
| 27 |
+
auth_method TEXT DEFAULT 'biometric',
|
| 28 |
+
locked_at INTEGER NOT NULL,
|
| 29 |
+
access_attempts INTEGER DEFAULT 0,
|
| 30 |
+
temporary_unlock_until INTEGER DEFAULT 0
|
| 31 |
+
)
|
| 32 |
+
""".trimIndent())
|
| 33 |
+
|
| 34 |
+
db.execSQL("""
|
| 35 |
+
CREATE TABLE IF NOT EXISTS access_log (
|
| 36 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 37 |
+
package_name TEXT NOT NULL,
|
| 38 |
+
app_name TEXT,
|
| 39 |
+
was_allowed INTEGER DEFAULT 0,
|
| 40 |
+
attempted_at INTEGER NOT NULL
|
| 41 |
+
)
|
| 42 |
+
""".trimIndent())
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
| 46 |
+
if (oldVersion < 2) {
|
| 47 |
+
db.execSQL("""
|
| 48 |
+
CREATE TABLE IF NOT EXISTS locked_apps (
|
| 49 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 50 |
+
package_name TEXT UNIQUE NOT NULL,
|
| 51 |
+
app_name TEXT NOT NULL,
|
| 52 |
+
auth_method TEXT DEFAULT 'biometric',
|
| 53 |
+
locked_at INTEGER NOT NULL,
|
| 54 |
+
access_attempts INTEGER DEFAULT 0,
|
| 55 |
+
temporary_unlock_until INTEGER DEFAULT 0
|
| 56 |
+
)
|
| 57 |
+
""".trimIndent())
|
| 58 |
+
|
| 59 |
+
db.execSQL("""
|
| 60 |
+
CREATE TABLE IF NOT EXISTS access_log (
|
| 61 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 62 |
+
package_name TEXT NOT NULL,
|
| 63 |
+
app_name TEXT,
|
| 64 |
+
was_allowed INTEGER DEFAULT 0,
|
| 65 |
+
attempted_at INTEGER NOT NULL
|
| 66 |
+
)
|
| 67 |
+
""".trimIndent())
|
| 68 |
+
}
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
fun lockedAppDao(): LockedAppDao = LockedAppDao(writableDatabase)
|
| 72 |
+
|
| 73 |
+
fun scamLogDao() = ScamLogDao(writableDatabase)
|
| 74 |
+
fun scamDomainDao() = ScamDomainDao(writableDatabase)
|
| 75 |
+
|
| 76 |
+
class ScamLogDao(private val db: SQLiteDatabase) {
|
| 77 |
+
fun getCountSince(timestamp: Long): Int {
|
| 78 |
+
val cursor = db.rawQuery("SELECT COUNT(*) FROM scam_log WHERE created_at >= ?", arrayOf(timestamp.toString()))
|
| 79 |
+
var count = 0
|
| 80 |
+
if (cursor.moveToFirst()) {
|
| 81 |
+
count = cursor.getInt(0)
|
| 82 |
+
}
|
| 83 |
+
cursor.close()
|
| 84 |
+
return count
|
| 85 |
+
}
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
class ScamDomainDao(private val db: SQLiteDatabase) {
|
| 89 |
+
fun getHitsSince(timestamp: Long): Int {
|
| 90 |
+
val cursor = db.rawQuery("SELECT COUNT(*) FROM vpn_events WHERE created_at >= ?", arrayOf(timestamp.toString()))
|
| 91 |
+
var count = 0
|
| 92 |
+
if (cursor.moveToFirst()) {
|
| 93 |
+
count = cursor.getInt(0)
|
| 94 |
+
}
|
| 95 |
+
cursor.close()
|
| 96 |
+
return count
|
| 97 |
+
}
|
| 98 |
+
}
|
| 99 |
+
}
|
android/app/src/main/kotlin/in/inmodel/premithus/PremithiusWidget.kt
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package `in`.inmodel.premithus
|
| 2 |
+
|
| 3 |
+
import android.app.PendingIntent
|
| 4 |
+
import android.appwidget.AppWidgetManager
|
| 5 |
+
import android.appwidget.AppWidgetProvider
|
| 6 |
+
import android.content.ComponentName
|
| 7 |
+
import android.content.Context
|
| 8 |
+
import android.content.Intent
|
| 9 |
+
import android.widget.RemoteViews
|
| 10 |
+
|
| 11 |
+
class PremithiusWidget : AppWidgetProvider() {
|
| 12 |
+
|
| 13 |
+
override fun onUpdate(
|
| 14 |
+
context: Context,
|
| 15 |
+
appWidgetManager: AppWidgetManager,
|
| 16 |
+
appWidgetIds: IntArray
|
| 17 |
+
) {
|
| 18 |
+
appWidgetIds.forEach { id ->
|
| 19 |
+
updateWidget(context, appWidgetManager, id)
|
| 20 |
+
}
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
companion object {
|
| 24 |
+
fun updateWidget(context: Context, manager: AppWidgetManager, id: Int) {
|
| 25 |
+
val views = RemoteViews(context.packageName, R.layout.widget_layout)
|
| 26 |
+
|
| 27 |
+
// Read data from SQLite directly (no Flutter needed)
|
| 28 |
+
val db = PremithiusDatabase.getInstance(context)
|
| 29 |
+
val today = System.currentTimeMillis() - 86400000L
|
| 30 |
+
|
| 31 |
+
// Tile 1: Mic/Camera RIGHT NOW
|
| 32 |
+
val telemetry = TelemetryHelper(context)
|
| 33 |
+
val activeSensors = try { telemetry.getActiveSensors() } catch (e: Exception) { emptyList() }
|
| 34 |
+
val sensorText = if (activeSensors.isNotEmpty())
|
| 35 |
+
"⚠️ ${activeSensors.first().appName} active"
|
| 36 |
+
else "✅ No sensors active"
|
| 37 |
+
val sensorColor = if (activeSensors.isNotEmpty())
|
| 38 |
+
R.color.widget_red else R.color.widget_green
|
| 39 |
+
views.setTextViewText(R.id.tile_sensor, sensorText)
|
| 40 |
+
views.setInt(R.id.tile_sensor, "setBackgroundColor",
|
| 41 |
+
context.getColor(sensorColor))
|
| 42 |
+
|
| 43 |
+
// Tile 2: Data sent last night (MB)
|
| 44 |
+
val dataUsage = try { telemetry.getSleepTimeDataUsage() } catch (e: Exception) { emptyList() }
|
| 45 |
+
val totalMb = dataUsage.sumOf { it.mbSent }.toInt()
|
| 46 |
+
val dataText = if (totalMb > 0) "📤 ${totalMb}MB sent" else "📤 0MB sent"
|
| 47 |
+
val dataColor = if (totalMb > 100) R.color.widget_red
|
| 48 |
+
else if (totalMb > 20) R.color.widget_amber
|
| 49 |
+
else R.color.widget_green
|
| 50 |
+
views.setTextViewText(R.id.tile_data, dataText)
|
| 51 |
+
views.setInt(R.id.tile_data, "setBackgroundColor",
|
| 52 |
+
context.getColor(dataColor))
|
| 53 |
+
|
| 54 |
+
// Tile 3: Suspicious permission spikes today
|
| 55 |
+
val permStats = try { telemetry.getPermissionStats(1) } catch (e: Exception) { emptyList() }
|
| 56 |
+
val spikeCount = permStats.count { it.totalAccesses > 5 }
|
| 57 |
+
val spikeText = if (spikeCount > 0) "⚠️ $spikeCount app spikes" else "✅ No spikes"
|
| 58 |
+
val spikeColor = if (spikeCount > 0) R.color.widget_amber else R.color.widget_green
|
| 59 |
+
views.setTextViewText(R.id.tile_perms, spikeText)
|
| 60 |
+
views.setInt(R.id.tile_perms, "setBackgroundColor",
|
| 61 |
+
context.getColor(spikeColor))
|
| 62 |
+
|
| 63 |
+
// Tile 4: Scam SMS + Domains blocked today (combined)
|
| 64 |
+
val scamCount = try { db.scamLogDao().getCountSince(today) } catch(e:Exception){ 0 }
|
| 65 |
+
val domainCount = try { db.scamDomainDao().getHitsSince(today) } catch(e:Exception){ 0 }
|
| 66 |
+
val totalBlocked = scamCount + domainCount
|
| 67 |
+
val blockedText = if (totalBlocked > 0)
|
| 68 |
+
"🚨 $totalBlocked blocked" else "✅ 0 blocked"
|
| 69 |
+
val blockedColor = if (totalBlocked > 0) R.color.widget_red else R.color.widget_green
|
| 70 |
+
views.setTextViewText(R.id.tile_blocked, blockedText)
|
| 71 |
+
views.setInt(R.id.tile_blocked, "setBackgroundColor",
|
| 72 |
+
context.getColor(blockedColor))
|
| 73 |
+
|
| 74 |
+
// Tap widget → open Premithius app
|
| 75 |
+
val launchIntent = context.packageManager.getLaunchIntentForPackage(context.packageName)
|
| 76 |
+
if (launchIntent != null) {
|
| 77 |
+
val pendingIntent = PendingIntent.getActivity(
|
| 78 |
+
context, 0,
|
| 79 |
+
launchIntent,
|
| 80 |
+
PendingIntent.FLAG_IMMUTABLE
|
| 81 |
+
)
|
| 82 |
+
views.setOnClickPendingIntent(R.id.widget_root, pendingIntent)
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
manager.updateAppWidget(id, views)
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
fun forceRefresh(context: Context) {
|
| 89 |
+
val intent = Intent(context, PremithiusWidget::class.java)
|
| 90 |
+
intent.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
| 91 |
+
val ids = AppWidgetManager.getInstance(context)
|
| 92 |
+
.getAppWidgetIds(ComponentName(context, PremithiusWidget::class.java))
|
| 93 |
+
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
|
| 94 |
+
context.sendBroadcast(intent)
|
| 95 |
+
}
|
| 96 |
+
}
|
| 97 |
+
}
|
android/app/src/main/kotlin/in/inmodel/premithus/SmsReceiver.kt
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package `in`.inmodel.premithus
|
| 2 |
+
|
| 3 |
+
import android.content.BroadcastReceiver
|
| 4 |
+
import android.content.Context
|
| 5 |
+
import android.content.Intent
|
| 6 |
+
import android.provider.Telephony
|
| 7 |
+
|
| 8 |
+
class SmsReceiver : BroadcastReceiver() {
|
| 9 |
+
override fun onReceive(context: Context, intent: Intent) {
|
| 10 |
+
if (intent.action == Telephony.Sms.Intents.SMS_RECEIVED_ACTION) {
|
| 11 |
+
val messages = Telephony.Sms.Intents.getMessagesFromIntent(intent)
|
| 12 |
+
for (sms in messages) {
|
| 13 |
+
val sender = sms.displayOriginatingAddress
|
| 14 |
+
val messageBody = sms.messageBody
|
| 15 |
+
|
| 16 |
+
// Start OverlayService to analyze SMS using Flutter
|
| 17 |
+
val serviceIntent = Intent(context, OverlayService::class.java).apply {
|
| 18 |
+
putExtra("sender", sender)
|
| 19 |
+
putExtra("messageBody", messageBody)
|
| 20 |
+
action = "in.inmodel.premithus.ACTION_ANALYZE_SMS"
|
| 21 |
+
}
|
| 22 |
+
context.startService(serviceIntent)
|
| 23 |
+
}
|
| 24 |
+
}
|
| 25 |
+
}
|
| 26 |
+
}
|
android/app/src/main/kotlin/in/inmodel/premithus/TelemetryHelper.kt
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package `in`.inmodel.premithus
|
| 2 |
+
|
| 3 |
+
import android.app.AppOpsManager
|
| 4 |
+
import android.app.usage.NetworkStats
|
| 5 |
+
import android.app.usage.NetworkStatsManager
|
| 6 |
+
import android.app.usage.UsageStatsManager
|
| 7 |
+
import android.content.Context
|
| 8 |
+
import android.content.pm.ApplicationInfo
|
| 9 |
+
import android.content.pm.PackageManager
|
| 10 |
+
import android.net.ConnectivityManager
|
| 11 |
+
import android.util.Log
|
| 12 |
+
|
| 13 |
+
class TelemetryHelper(private val context: Context) {
|
| 14 |
+
|
| 15 |
+
// ── Mic/Camera real-time access ─────────────────────────────
|
| 16 |
+
fun getActiveSensors(): List<SensorAccess> {
|
| 17 |
+
val appOps = context.getSystemService(AppOpsManager::class.java)
|
| 18 |
+
val result = mutableListOf<SensorAccess>()
|
| 19 |
+
|
| 20 |
+
val packages = context.packageManager
|
| 21 |
+
.getInstalledApplications(PackageManager.GET_META_DATA)
|
| 22 |
+
|
| 23 |
+
for (app in packages) {
|
| 24 |
+
val micOp = appOps.unsafeCheckOpNoThrow(
|
| 25 |
+
AppOpsManager.OPSTR_RECORD_AUDIO,
|
| 26 |
+
app.uid, app.packageName
|
| 27 |
+
)
|
| 28 |
+
val camOp = appOps.unsafeCheckOpNoThrow(
|
| 29 |
+
AppOpsManager.OPSTR_CAMERA,
|
| 30 |
+
app.uid, app.packageName
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
// MODE_RUNNING = currently active (Android 10+)
|
| 34 |
+
if (micOp == AppOpsManager.MODE_RUNNING) {
|
| 35 |
+
result.add(SensorAccess(app.packageName,
|
| 36 |
+
getAppName(app.packageName), "microphone", true))
|
| 37 |
+
}
|
| 38 |
+
if (camOp == AppOpsManager.MODE_RUNNING) {
|
| 39 |
+
result.add(SensorAccess(app.packageName,
|
| 40 |
+
getAppName(app.packageName), "camera", true))
|
| 41 |
+
}
|
| 42 |
+
}
|
| 43 |
+
return result
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
// ── Permission access counts (last 7 days) ──────────────────
|
| 47 |
+
fun getPermissionStats(days: Int = 7): List<AppPermissionStat> {
|
| 48 |
+
val appOps = context.getSystemService(AppOpsManager::class.java)
|
| 49 |
+
val result = mutableListOf<AppPermissionStat>()
|
| 50 |
+
val since = System.currentTimeMillis() - (days * 86400000L)
|
| 51 |
+
|
| 52 |
+
val ops = listOf(
|
| 53 |
+
AppOpsManager.OPSTR_RECORD_AUDIO to "microphone",
|
| 54 |
+
AppOpsManager.OPSTR_CAMERA to "camera",
|
| 55 |
+
AppOpsManager.OPSTR_FINE_LOCATION to "location",
|
| 56 |
+
AppOpsManager.OPSTR_READ_CONTACTS to "contacts",
|
| 57 |
+
AppOpsManager.OPSTR_READ_SMS to "SMS"
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
val packages = context.packageManager
|
| 61 |
+
.getInstalledApplications(PackageManager.GET_META_DATA)
|
| 62 |
+
|
| 63 |
+
for (app in packages) {
|
| 64 |
+
val stats = mutableMapOf<String, Int>()
|
| 65 |
+
for ((opStr, label) in ops) {
|
| 66 |
+
try {
|
| 67 |
+
val pkgOps = appOps.getOpsForPackage(
|
| 68 |
+
app.uid, app.packageName, arrayOf(opStr)
|
| 69 |
+
)
|
| 70 |
+
val count = pkgOps?.firstOrNull()?.ops
|
| 71 |
+
?.filter { it.time > since }
|
| 72 |
+
?.size ?: 0
|
| 73 |
+
if (count > 0) stats[label] = count
|
| 74 |
+
} catch (e: Exception) { /* skip */ }
|
| 75 |
+
}
|
| 76 |
+
if (stats.isNotEmpty()) {
|
| 77 |
+
result.add(AppPermissionStat(
|
| 78 |
+
packageName = app.packageName,
|
| 79 |
+
appName = getAppName(app.packageName),
|
| 80 |
+
accessCounts = stats,
|
| 81 |
+
isSystemApp = (app.flags and ApplicationInfo.FLAG_SYSTEM) != 0
|
| 82 |
+
))
|
| 83 |
+
}
|
| 84 |
+
}
|
| 85 |
+
return result.sortedByDescending { it.totalAccesses }
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
// ── Network data usage (last 24 hours) ──────────────────────
|
| 89 |
+
fun getSleepTimeDataUsage(): List<AppDataUsage> {
|
| 90 |
+
if (!hasUsageStatsPermission()) return emptyList()
|
| 91 |
+
|
| 92 |
+
val networkStats = context.getSystemService(NetworkStatsManager::class.java)
|
| 93 |
+
|
| 94 |
+
val now = System.currentTimeMillis()
|
| 95 |
+
val yesterday = now - 86400000L
|
| 96 |
+
|
| 97 |
+
val result = mutableListOf<AppDataUsage>()
|
| 98 |
+
|
| 99 |
+
try {
|
| 100 |
+
val summary = networkStats.querySummary(
|
| 101 |
+
ConnectivityManager.TYPE_MOBILE, null, yesterday, now
|
| 102 |
+
)
|
| 103 |
+
val bucket = NetworkStats.Bucket()
|
| 104 |
+
val appTotals = mutableMapOf<Int, Long>() // uid → bytes sent
|
| 105 |
+
|
| 106 |
+
while (summary.hasNextBucket()) {
|
| 107 |
+
summary.getNextBucket(bucket)
|
| 108 |
+
val uid = bucket.uid
|
| 109 |
+
if (uid > 1000) { // skip system UIDs
|
| 110 |
+
appTotals[uid] = (appTotals[uid] ?: 0) + bucket.txBytes
|
| 111 |
+
}
|
| 112 |
+
}
|
| 113 |
+
summary.close()
|
| 114 |
+
|
| 115 |
+
val pm = context.packageManager
|
| 116 |
+
for ((uid, bytes) in appTotals) {
|
| 117 |
+
if (bytes < 100_000) continue // skip < 100KB
|
| 118 |
+
try {
|
| 119 |
+
val pkgs = pm.getPackagesForUid(uid) ?: continue
|
| 120 |
+
val pkg = pkgs.first()
|
| 121 |
+
val appName = getAppName(pkg)
|
| 122 |
+
result.add(AppDataUsage(
|
| 123 |
+
packageName = pkg,
|
| 124 |
+
appName = appName,
|
| 125 |
+
bytesSent = bytes,
|
| 126 |
+
mbSent = bytes / 1_048_576.0
|
| 127 |
+
))
|
| 128 |
+
} catch (e: Exception) { /* skip */ }
|
| 129 |
+
}
|
| 130 |
+
} catch (e: Exception) {
|
| 131 |
+
Log.e("TelemetryHelper", "Network stats error: ${e.message}")
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
return result.sortedByDescending { it.bytesSent }
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
// ── Story generator ─────────────────────────────────────────
|
| 138 |
+
fun generatePrivacyStories(
|
| 139 |
+
sensors: List<SensorAccess>,
|
| 140 |
+
permissions: List<AppPermissionStat>,
|
| 141 |
+
dataUsage: List<AppDataUsage>
|
| 142 |
+
): List<PrivacyStory> {
|
| 143 |
+
val stories = mutableListOf<PrivacyStory>()
|
| 144 |
+
|
| 145 |
+
for (sensor in sensors) {
|
| 146 |
+
stories.add(PrivacyStory(
|
| 147 |
+
severity = "CRITICAL",
|
| 148 |
+
emoji = if (sensor.sensor == "microphone") "🎙️" else "📷",
|
| 149 |
+
english = "${sensor.appName} is ${sensor.sensor} RIGHT NOW",
|
| 150 |
+
hindi = "${sensor.appName} अभी आपका ${
|
| 151 |
+
if (sensor.sensor == "microphone") "माइक" else "कैमरा"
|
| 152 |
+
} इस्तेमाल कर रहा है",
|
| 153 |
+
packageName = sensor.packageName
|
| 154 |
+
))
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
for (app in permissions.take(5)) {
|
| 158 |
+
val micCount = app.accessCounts["microphone"] ?: 0
|
| 159 |
+
val locCount = app.accessCounts["location"] ?: 0
|
| 160 |
+
val contactCount = app.accessCounts["contacts"] ?: 0
|
| 161 |
+
|
| 162 |
+
if (micCount > 10) {
|
| 163 |
+
stories.add(PrivacyStory(
|
| 164 |
+
severity = "HIGH",
|
| 165 |
+
emoji = "🎙️",
|
| 166 |
+
english = "${app.appName} accessed your microphone $micCount times this week",
|
| 167 |
+
hindi = "${app.appName} ने इस हफ्ते $micCount बार माइक एक्सेस किया",
|
| 168 |
+
packageName = app.packageName
|
| 169 |
+
))
|
| 170 |
+
}
|
| 171 |
+
if (locCount > 20) {
|
| 172 |
+
stories.add(PrivacyStory(
|
| 173 |
+
severity = "HIGH",
|
| 174 |
+
emoji = "📍",
|
| 175 |
+
english = "${app.appName} tracked your location $locCount times this week",
|
| 176 |
+
hindi = "${app.appName} ने $locCount बार आपकी लोकेशन ट्रैक की",
|
| 177 |
+
packageName = app.packageName
|
| 178 |
+
))
|
| 179 |
+
}
|
| 180 |
+
if (contactCount > 0 && !isCommunicationApp(app.packageName)) {
|
| 181 |
+
stories.add(PrivacyStory(
|
| 182 |
+
severity = "MEDIUM",
|
| 183 |
+
emoji = "👥",
|
| 184 |
+
english = "${app.appName} read your contacts — this app doesn't need them",
|
| 185 |
+
hindi = "${app.appName} ने आपके contacts पढ़े — इस app को इसकी जरूरत नहीं",
|
| 186 |
+
packageName = app.packageName
|
| 187 |
+
))
|
| 188 |
+
}
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
for (app in dataUsage.take(3)) {
|
| 192 |
+
if (app.mbSent > 50) {
|
| 193 |
+
stories.add(PrivacyStory(
|
| 194 |
+
severity = "MEDIUM",
|
| 195 |
+
emoji = "📤",
|
| 196 |
+
english = "${app.appName} sent ${app.mbSent.toInt()} MB of your data",
|
| 197 |
+
hindi = "${app.appName} ने आपका ${app.mbSent.toInt()} MB डेटा भेजा",
|
| 198 |
+
packageName = app.packageName
|
| 199 |
+
))
|
| 200 |
+
}
|
| 201 |
+
}
|
| 202 |
+
|
| 203 |
+
if (stories.isEmpty()) {
|
| 204 |
+
stories.add(PrivacyStory(
|
| 205 |
+
severity = "SAFE",
|
| 206 |
+
emoji = "✅",
|
| 207 |
+
english = "No suspicious activity detected in the last 24 hours",
|
| 208 |
+
hindi = "पिछले 24 घंटों में कोई संदिग्ध गतिविधि नहीं मिली",
|
| 209 |
+
packageName = ""
|
| 210 |
+
))
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
return stories.sortedBy {
|
| 214 |
+
when (it.severity) { "CRITICAL" -> 0; "HIGH" -> 1; "MEDIUM" -> 2; else -> 3 }
|
| 215 |
+
}
|
| 216 |
+
}
|
| 217 |
+
|
| 218 |
+
fun hasUsageStatsPermission(): Boolean {
|
| 219 |
+
val appOps = context.getSystemService(AppOpsManager::class.java)
|
| 220 |
+
val mode = appOps.unsafeCheckOpNoThrow(
|
| 221 |
+
AppOpsManager.OPSTR_GET_USAGE_STATS,
|
| 222 |
+
android.os.Process.myUid(), context.packageName
|
| 223 |
+
)
|
| 224 |
+
return mode == AppOpsManager.MODE_ALLOWED
|
| 225 |
+
}
|
| 226 |
+
|
| 227 |
+
private fun getAppName(packageName: String): String {
|
| 228 |
+
return try {
|
| 229 |
+
val ai = context.packageManager.getApplicationInfo(packageName, 0)
|
| 230 |
+
context.packageManager.getApplicationLabel(ai).toString()
|
| 231 |
+
} catch (e: Exception) { packageName }
|
| 232 |
+
}
|
| 233 |
+
|
| 234 |
+
private fun isCommunicationApp(pkg: String): Boolean {
|
| 235 |
+
return pkg.contains("whatsapp") || pkg.contains("telegram") ||
|
| 236 |
+
pkg.contains("signal") || pkg.contains("gmail") ||
|
| 237 |
+
pkg.contains("message") || pkg.contains("dialer")
|
| 238 |
+
}
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
// Data classes
|
| 242 |
+
data class SensorAccess(val packageName: String, val appName: String,
|
| 243 |
+
val sensor: String, val isActive: Boolean)
|
| 244 |
+
data class AppPermissionStat(val packageName: String, val appName: String,
|
| 245 |
+
val accessCounts: Map<String, Int>, val isSystemApp: Boolean) {
|
| 246 |
+
val totalAccesses get() = accessCounts.values.sum()
|
| 247 |
+
}
|
| 248 |
+
data class AppDataUsage(val packageName: String, val appName: String,
|
| 249 |
+
val bytesSent: Long, val mbSent: Double)
|
| 250 |
+
data class PrivacyStory(val severity: String, val emoji: String,
|
| 251 |
+
val english: String, val hindi: String, val packageName: String)
|
android/app/src/main/res/drawable-v21/launch_background.xml
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="utf-8"?>
|
| 2 |
+
<!-- Modify this file to customize your launch splash screen -->
|
| 3 |
+
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
| 4 |
+
<item android:drawable="?android:colorBackground" />
|
| 5 |
+
|
| 6 |
+
<!-- You can insert your own image assets here -->
|
| 7 |
+
<!-- <item>
|
| 8 |
+
<bitmap
|
| 9 |
+
android:gravity="center"
|
| 10 |
+
android:src="@mipmap/launch_image" />
|
| 11 |
+
</item> -->
|
| 12 |
+
</layer-list>
|
android/app/src/main/res/drawable/launch_background.xml
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="utf-8"?>
|
| 2 |
+
<!-- Modify this file to customize your launch splash screen -->
|
| 3 |
+
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
| 4 |
+
<item android:drawable="@android:color/white" />
|
| 5 |
+
|
| 6 |
+
<!-- You can insert your own image assets here -->
|
| 7 |
+
<!-- <item>
|
| 8 |
+
<bitmap
|
| 9 |
+
android:gravity="center"
|
| 10 |
+
android:src="@mipmap/launch_image" />
|
| 11 |
+
</item> -->
|
| 12 |
+
</layer-list>
|
android/app/src/main/res/layout/widget_4x2.xml
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="utf-8"?>
|
| 2 |
+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
| 3 |
+
android:layout_width="match_parent"
|
| 4 |
+
android:layout_height="match_parent"
|
| 5 |
+
android:orientation="vertical"
|
| 6 |
+
android:background="#2E2E2E"
|
| 7 |
+
android:padding="8dp">
|
| 8 |
+
|
| 9 |
+
<!-- Grid for Threat Metrics -->
|
| 10 |
+
<LinearLayout
|
| 11 |
+
android:layout_width="match_parent"
|
| 12 |
+
android:layout_height="wrap_content"
|
| 13 |
+
android:orientation="horizontal"
|
| 14 |
+
android:weightSum="4">
|
| 15 |
+
|
| 16 |
+
<!-- Tile 1 -->
|
| 17 |
+
<LinearLayout
|
| 18 |
+
android:id="@+id/mic_tile"
|
| 19 |
+
android:layout_width="0dp"
|
| 20 |
+
android:layout_height="wrap_content"
|
| 21 |
+
android:layout_weight="1"
|
| 22 |
+
android:orientation="vertical"
|
| 23 |
+
android:gravity="center"
|
| 24 |
+
android:padding="8dp">
|
| 25 |
+
<TextView android:text="MIC SPY" android:textSize="10sp" android:textColor="#888" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
|
| 26 |
+
<TextView android:id="@+id/mic_value" android:text="0" android:textSize="18sp" android:textStyle="bold" android:textColor="#FFF" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
|
| 27 |
+
</LinearLayout>
|
| 28 |
+
|
| 29 |
+
<!-- Tile 2 -->
|
| 30 |
+
<LinearLayout
|
| 31 |
+
android:layout_width="0dp"
|
| 32 |
+
android:layout_height="wrap_content"
|
| 33 |
+
android:layout_weight="1"
|
| 34 |
+
android:orientation="vertical"
|
| 35 |
+
android:gravity="center"
|
| 36 |
+
android:padding="8dp">
|
| 37 |
+
<TextView android:text="NIGHT DATA" android:textSize="10sp" android:textColor="#888" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
|
| 38 |
+
<TextView android:id="@+id/data_value" android:text="0 MB" android:textSize="18sp" android:textStyle="bold" android:textColor="#FFF" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
|
| 39 |
+
</LinearLayout>
|
| 40 |
+
|
| 41 |
+
<!-- Tile 3 -->
|
| 42 |
+
<LinearLayout
|
| 43 |
+
android:layout_width="0dp"
|
| 44 |
+
android:layout_height="wrap_content"
|
| 45 |
+
android:layout_weight="1"
|
| 46 |
+
android:orientation="vertical"
|
| 47 |
+
android:gravity="center"
|
| 48 |
+
android:padding="8dp">
|
| 49 |
+
<TextView android:text="PERMISSIONS" android:textSize="10sp" android:textColor="#888" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
|
| 50 |
+
<TextView android:id="@+id/perm_value" android:text="0" android:textSize="18sp" android:textStyle="bold" android:textColor="#FFF" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
|
| 51 |
+
</LinearLayout>
|
| 52 |
+
|
| 53 |
+
<!-- Tile 4 -->
|
| 54 |
+
<LinearLayout
|
| 55 |
+
android:layout_width="0dp"
|
| 56 |
+
android:layout_height="wrap_content"
|
| 57 |
+
android:layout_weight="1"
|
| 58 |
+
android:orientation="vertical"
|
| 59 |
+
android:gravity="center"
|
| 60 |
+
android:padding="8dp">
|
| 61 |
+
<TextView android:text="SCAMS" android:textSize="10sp" android:textColor="#888" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
|
| 62 |
+
<TextView android:id="@+id/scam_value" android:text="0" android:textSize="18sp" android:textStyle="bold" android:textColor="#FFF" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
|
| 63 |
+
</LinearLayout>
|
| 64 |
+
</LinearLayout>
|
| 65 |
+
|
| 66 |
+
<!-- Alert Banner -->
|
| 67 |
+
<LinearLayout
|
| 68 |
+
android:id="@+id/alert_banner"
|
| 69 |
+
android:layout_width="match_parent"
|
| 70 |
+
android:layout_height="wrap_content"
|
| 71 |
+
android:orientation="horizontal"
|
| 72 |
+
android:background="#D32F2F"
|
| 73 |
+
android:visibility="gone"
|
| 74 |
+
android:padding="8dp"
|
| 75 |
+
android:layout_marginTop="8dp">
|
| 76 |
+
<TextView
|
| 77 |
+
android:id="@+id/alert_text"
|
| 78 |
+
android:layout_width="match_parent"
|
| 79 |
+
android:layout_height="wrap_content"
|
| 80 |
+
android:text="Alert Message Here"
|
| 81 |
+
android:textColor="#FFFFFF"
|
| 82 |
+
android:textSize="12sp"
|
| 83 |
+
android:textStyle="bold" />
|
| 84 |
+
</LinearLayout>
|
| 85 |
+
</LinearLayout>
|
android/app/src/main/res/layout/widget_layout.xml
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="utf-8"?>
|
| 2 |
+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
| 3 |
+
android:id="@+id/widget_root"
|
| 4 |
+
android:layout_width="match_parent"
|
| 5 |
+
android:layout_height="match_parent"
|
| 6 |
+
android:orientation="horizontal"
|
| 7 |
+
android:background="#CC1A1A2E"
|
| 8 |
+
android:padding="8dp">
|
| 9 |
+
|
| 10 |
+
<LinearLayout
|
| 11 |
+
android:layout_width="0dp"
|
| 12 |
+
android:layout_height="match_parent"
|
| 13 |
+
android:layout_weight="1"
|
| 14 |
+
android:orientation="vertical"
|
| 15 |
+
android:padding="6dp"
|
| 16 |
+
android:layout_marginEnd="4dp">
|
| 17 |
+
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="🎙️ Mic/Cam" android:textColor="#FFFFFF" android:textSize="12sp"/>
|
| 18 |
+
<TextView android:id="@+id/tile_sensor" android:layout_width="match_parent" android:layout_height="match_parent" android:textColor="#FFFFFF" android:textSize="14sp" android:textStyle="bold" android:layout_marginTop="4dp" android:gravity="center" android:padding="4dp"/>
|
| 19 |
+
</LinearLayout>
|
| 20 |
+
|
| 21 |
+
<LinearLayout
|
| 22 |
+
android:layout_width="0dp"
|
| 23 |
+
android:layout_height="match_parent"
|
| 24 |
+
android:layout_weight="1"
|
| 25 |
+
android:orientation="vertical"
|
| 26 |
+
android:padding="6dp"
|
| 27 |
+
android:layout_marginEnd="4dp">
|
| 28 |
+
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="📤 Data Sent" android:textColor="#FFFFFF" android:textSize="12sp"/>
|
| 29 |
+
<TextView android:id="@+id/tile_data" android:layout_width="match_parent" android:layout_height="match_parent" android:textColor="#FFFFFF" android:textSize="14sp" android:textStyle="bold" android:layout_marginTop="4dp" android:gravity="center" android:padding="4dp"/>
|
| 30 |
+
</LinearLayout>
|
| 31 |
+
|
| 32 |
+
<LinearLayout
|
| 33 |
+
android:layout_width="0dp"
|
| 34 |
+
android:layout_height="match_parent"
|
| 35 |
+
android:layout_weight="1"
|
| 36 |
+
android:orientation="vertical"
|
| 37 |
+
android:padding="6dp"
|
| 38 |
+
android:layout_marginEnd="4dp">
|
| 39 |
+
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="⚠️ App Spikes" android:textColor="#FFFFFF" android:textSize="12sp"/>
|
| 40 |
+
<TextView android:id="@+id/tile_perms" android:layout_width="match_parent" android:layout_height="match_parent" android:textColor="#FFFFFF" android:textSize="14sp" android:textStyle="bold" android:layout_marginTop="4dp" android:gravity="center" android:padding="4dp"/>
|
| 41 |
+
</LinearLayout>
|
| 42 |
+
|
| 43 |
+
<LinearLayout
|
| 44 |
+
android:layout_width="0dp"
|
| 45 |
+
android:layout_height="match_parent"
|
| 46 |
+
android:layout_weight="1"
|
| 47 |
+
android:orientation="vertical"
|
| 48 |
+
android:padding="6dp">
|
| 49 |
+
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="🛡️ Blocked" android:textColor="#FFFFFF" android:textSize="12sp"/>
|
| 50 |
+
<TextView android:id="@+id/tile_blocked" android:layout_width="match_parent" android:layout_height="match_parent" android:textColor="#FFFFFF" android:textSize="14sp" android:textStyle="bold" android:layout_marginTop="4dp" android:gravity="center" android:padding="4dp"/>
|
| 51 |
+
</LinearLayout>
|
| 52 |
+
</LinearLayout>
|
android/app/src/main/res/values-night/styles.xml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="utf-8"?>
|
| 2 |
+
<resources>
|
| 3 |
+
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
| 4 |
+
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
| 5 |
+
<!-- Show a splash screen on the activity. Automatically removed when
|
| 6 |
+
the Flutter engine draws its first frame -->
|
| 7 |
+
<item name="android:windowBackground">@drawable/launch_background</item>
|
| 8 |
+
</style>
|
| 9 |
+
<!-- Theme applied to the Android Window as soon as the process has started.
|
| 10 |
+
This theme determines the color of the Android Window while your
|
| 11 |
+
Flutter UI initializes, as well as behind your Flutter UI while its
|
| 12 |
+
running.
|
| 13 |
+
|
| 14 |
+
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
| 15 |
+
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
| 16 |
+
<item name="android:windowBackground">?android:colorBackground</item>
|
| 17 |
+
</style>
|
| 18 |
+
</resources>
|
android/app/src/main/res/values/colors.xml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="utf-8"?>
|
| 2 |
+
<resources>
|
| 3 |
+
<color name="widget_red">#FFCC2222</color>
|
| 4 |
+
<color name="widget_amber">#FFCC7700</color>
|
| 5 |
+
<color name="widget_green">#FF227722</color>
|
| 6 |
+
<color name="widget_neutral">#FF333355</color>
|
| 7 |
+
</resources>
|
android/app/src/main/res/values/strings.xml
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="utf-8"?>
|
| 2 |
+
<resources>
|
| 3 |
+
<string name="app_name">Premithius</string>
|
| 4 |
+
<string name="accessibility_label">Premithius Shield</string>
|
| 5 |
+
<string name="accessibility_description">Premithius uses Accessibility to detect scam apps opening and protect your privacy. No data leaves your device.</string>
|
| 6 |
+
<string name="accessibility_summary">App protection and scam detection</string>
|
| 7 |
+
<string name="widget_description">Premithius live security summary</string>
|
| 8 |
+
</resources>
|
android/app/src/main/res/values/styles.xml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="utf-8"?>
|
| 2 |
+
<resources>
|
| 3 |
+
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
| 4 |
+
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
| 5 |
+
<!-- Show a splash screen on the activity. Automatically removed when
|
| 6 |
+
the Flutter engine draws its first frame -->
|
| 7 |
+
<item name="android:windowBackground">@drawable/launch_background</item>
|
| 8 |
+
</style>
|
| 9 |
+
<!-- Theme applied to the Android Window as soon as the process has started.
|
| 10 |
+
This theme determines the color of the Android Window while your
|
| 11 |
+
Flutter UI initializes, as well as behind your Flutter UI while its
|
| 12 |
+
running.
|
| 13 |
+
|
| 14 |
+
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
| 15 |
+
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
| 16 |
+
<item name="android:windowBackground">?android:colorBackground</item>
|
| 17 |
+
</style>
|
| 18 |
+
</resources>
|
android/app/src/main/res/xml/accessibility_service_config.xml
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="utf-8"?>
|
| 2 |
+
<accessibility-service
|
| 3 |
+
xmlns:android="http://schemas.android.com/apk/res/android"
|
| 4 |
+
android:accessibilityEventTypes="typeWindowStateChanged|typeWindowContentChanged"
|
| 5 |
+
android:accessibilityFeedbackType="feedbackGeneric"
|
| 6 |
+
android:accessibilityFlags="flagReportViewIds|flagRetrieveInteractiveWindows"
|
| 7 |
+
android:canRetrieveWindowContent="true"
|
| 8 |
+
android:description="@string/accessibility_description"
|
| 9 |
+
android:notificationTimeout="100"
|
| 10 |
+
android:settingsActivity="in.inmodel.premithus.MainActivity"
|
| 11 |
+
android:summary="@string/accessibility_summary"/>
|
android/app/src/main/res/xml/method.xml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="utf-8"?>
|
| 2 |
+
<input-method xmlns:android="http://schemas.android.com/apk/res/android"
|
| 3 |
+
android:supportsInlineSuggestions="true">
|
| 4 |
+
<subtype
|
| 5 |
+
android:imeSubtypeLocale="en_US"
|
| 6 |
+
android:imeSubtypeMode="keyboard" />
|
| 7 |
+
</input-method>
|
android/app/src/main/res/xml/premithius_widget_info.xml
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="utf-8"?>
|
| 2 |
+
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
|
| 3 |
+
android:minWidth="250dp"
|
| 4 |
+
android:minHeight="110dp"
|
| 5 |
+
android:targetCellWidth="4"
|
| 6 |
+
android:targetCellHeight="2"
|
| 7 |
+
android:updatePeriodMillis="1800000"
|
| 8 |
+
android:initialLayout="@layout/widget_layout"
|
| 9 |
+
android:widgetCategory="home_screen"
|
| 10 |
+
android:resizeMode="horizontal|vertical"
|
| 11 |
+
android:description="@string/widget_description">
|
| 12 |
+
</appwidget-provider>
|
android/app/src/profile/AndroidManifest.xml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
| 2 |
+
<!-- The INTERNET permission is required for development. Specifically,
|
| 3 |
+
the Flutter tool needs it to communicate with the running application
|
| 4 |
+
to allow setting breakpoints, to provide hot reload, etc.
|
| 5 |
+
-->
|
| 6 |
+
<uses-permission android:name="android.permission.INTERNET"/>
|
| 7 |
+
</manifest>
|
android/build.gradle.kts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
allprojects {
|
| 2 |
+
repositories {
|
| 3 |
+
google()
|
| 4 |
+
mavenCentral()
|
| 5 |
+
maven { url = uri("https://jitpack.io") }
|
| 6 |
+
}
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
val newBuildDir: Directory =
|
| 10 |
+
rootProject.layout.buildDirectory
|
| 11 |
+
.dir("../../build")
|
| 12 |
+
.get()
|
| 13 |
+
rootProject.layout.buildDirectory.value(newBuildDir)
|
| 14 |
+
|
| 15 |
+
subprojects {
|
| 16 |
+
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
|
| 17 |
+
project.layout.buildDirectory.value(newSubprojectBuildDir)
|
| 18 |
+
}
|
| 19 |
+
subprojects {
|
| 20 |
+
project.evaluationDependsOn(":app")
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
tasks.register<Delete>("clean") {
|
| 24 |
+
delete(rootProject.layout.buildDirectory)
|
| 25 |
+
}
|
android/build/reports/problems/problems-report.html
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
android/gradle.properties
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
| 2 |
+
android.useAndroidX=true
|
android/gradle/wrapper/gradle-wrapper.properties
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
distributionBase=GRADLE_USER_HOME
|
| 2 |
+
distributionPath=wrapper/dists
|
| 3 |
+
zipStoreBase=GRADLE_USER_HOME
|
| 4 |
+
zipStorePath=wrapper/dists
|
| 5 |
+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip
|
android/settings.gradle.kts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
pluginManagement {
|
| 2 |
+
val flutterSdkPath =
|
| 3 |
+
run {
|
| 4 |
+
val properties = java.util.Properties()
|
| 5 |
+
file("local.properties").inputStream().use { properties.load(it) }
|
| 6 |
+
val flutterSdkPath = properties.getProperty("flutter.sdk")
|
| 7 |
+
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
|
| 8 |
+
flutterSdkPath
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
|
| 12 |
+
|
| 13 |
+
repositories {
|
| 14 |
+
google()
|
| 15 |
+
mavenCentral()
|
| 16 |
+
gradlePluginPortal()
|
| 17 |
+
}
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
plugins {
|
| 21 |
+
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
| 22 |
+
id("com.android.application") version "8.11.1" apply false
|
| 23 |
+
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
include(":app")
|
assets/blocklist/india_scam_domains.txt
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# KYC Fraud domains
|
| 2 |
+
hdfcbank-kyc.net
|
| 3 |
+
sbi-kyc-verify.top
|
| 4 |
+
icici-kyc-update.xyz
|
| 5 |
+
axis-kyc-expire.click
|
| 6 |
+
pnb-kyc-alert.com
|
| 7 |
+
bob-kyc-update.in
|
| 8 |
+
|
| 9 |
+
# Fake bank portals
|
| 10 |
+
netbanking-hdfc.com
|
| 11 |
+
sbi-netbanking.xyz
|
| 12 |
+
icici-ibank.top
|
| 13 |
+
hdfc-secure-login.net
|
| 14 |
+
axisbank-online-secure.com
|
| 15 |
+
sbi-reward-points.online
|
| 16 |
+
hdfc-reward-redeem.in
|
| 17 |
+
icici-card-block.xyz
|
| 18 |
+
|
| 19 |
+
# UPI Fraud domains
|
| 20 |
+
upi-reward-claim.xyz
|
| 21 |
+
paytm-cashback-offer.top
|
| 22 |
+
phonepe-winner.click
|
| 23 |
+
gpay-prize.tk
|
| 24 |
+
bhim-upi-cashback.in
|
| 25 |
+
upi-refund-process.top
|
| 26 |
+
phonepe-support-care.com
|
| 27 |
+
|
| 28 |
+
# Lottery / Prize scams
|
| 29 |
+
india-lottery-winner.net
|
| 30 |
+
bsnl-prize-claim.xyz
|
| 31 |
+
jio-recharge-free.top
|
| 32 |
+
airtel-winner-prize.click
|
| 33 |
+
kbc-lottery-winner.in
|
| 34 |
+
tata-safari-winner.club
|
| 35 |
+
mahindra-thar-prize.com
|
| 36 |
+
|
| 37 |
+
# Fake government portals
|
| 38 |
+
pm-kisan-apply.xyz
|
| 39 |
+
aadhar-update-now.top
|
| 40 |
+
pan-verify-online.click
|
| 41 |
+
income-tax-refund.net
|
| 42 |
+
epfo-claim-status.in
|
| 43 |
+
passport-sewa-india.org
|
| 44 |
+
voter-id-verify.online
|
| 45 |
+
cowin-certificate-download.click
|
| 46 |
+
|
| 47 |
+
# Investment scams
|
| 48 |
+
trading-profit-india.xyz
|
| 49 |
+
stock-tip-guaranteed.top
|
| 50 |
+
crypto-india-profit.click
|
| 51 |
+
bitcoin-doubler.in
|
| 52 |
+
nse-bse-tips.club
|
| 53 |
+
zerodha-trading-bonus.online
|
| 54 |
+
|
| 55 |
+
# Loan app harassment
|
| 56 |
+
insta-loan-now.xyz
|
| 57 |
+
quick-cash-app.top
|
| 58 |
+
easy-money-india.click
|
| 59 |
+
dhani-loan-approve.in
|
| 60 |
+
kreditbee-instant.site
|
| 61 |
+
navi-loan-offer.xyz
|
| 62 |
+
|
| 63 |
+
# Extortion / Threats
|
| 64 |
+
cyber-crime-notice.in
|
| 65 |
+
cbi-investigation-portal.org
|
| 66 |
+
rbi-penalty-payment.com
|
| 67 |
+
customs-clearance-fee.xyz
|
| 68 |
+
trai-number-block.top
|
assets/seed_embeddings.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
build_err.txt
ADDED
|
@@ -0,0 +1,296 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Running Gradle task 'assembleDebug'...
|
| 2 |
+
|
| 3 |
+
FAILURE: Build failed with an exception.
|
| 4 |
+
|
| 5 |
+
* What went wrong:
|
| 6 |
+
Execution failed for task ':llama_sdk:configureCMakeDebug[arm64-v8a]'.
|
| 7 |
+
> [CXX1429] error when building with cmake using /Users/friday/.pub-cache/git/lcpp-7c2f8423e87781859acc514cd0aa36554f423163/android/CMakeLists.txt: -- The C compiler identification is Clang 18.0.1
|
| 8 |
+
-- The CXX compiler identification is Clang 18.0.1
|
| 9 |
+
-- Detecting C compiler ABI info
|
| 10 |
+
-- Detecting C compiler ABI info - done
|
| 11 |
+
-- Check for working C compiler: /Users/friday/Library/Android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang - skipped
|
| 12 |
+
-- Detecting C compile features
|
| 13 |
+
-- Detecting C compile features - done
|
| 14 |
+
-- Detecting CXX compiler ABI info
|
| 15 |
+
-- Detecting CXX compiler ABI info - done
|
| 16 |
+
-- Check for working CXX compiler: /Users/friday/Library/Android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++ - skipped
|
| 17 |
+
-- Detecting CXX compile features
|
| 18 |
+
-- Detecting CXX compile features - done
|
| 19 |
+
-- Configuring incomplete, errors occurred!
|
| 20 |
+
See also "/Users/friday/.pub-cache/git/lcpp-7c2f8423e87781859acc514cd0aa36554f423163/android/.cxx/Debug/1y5h19dr/arm64-v8a/CMakeFiles/CMakeOutput.log".
|
| 21 |
+
|
| 22 |
+
C++ build system [configure] failed while executing:
|
| 23 |
+
/Users/friday/Library/Android/sdk/cmake/3.22.1/bin/cmake \
|
| 24 |
+
-H/Users/friday/.pub-cache/git/lcpp-7c2f8423e87781859acc514cd0aa36554f423163/android \
|
| 25 |
+
-DCMAKE_SYSTEM_NAME=Android \
|
| 26 |
+
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
| 27 |
+
-DCMAKE_SYSTEM_VERSION=23 \
|
| 28 |
+
-DANDROID_PLATFORM=android-23 \
|
| 29 |
+
-DANDROID_ABI=arm64-v8a \
|
| 30 |
+
-DCMAKE_ANDROID_ARCH_ABI=arm64-v8a \
|
| 31 |
+
-DANDROID_NDK=/Users/friday/Library/Android/sdk/ndk/27.0.12077973 \
|
| 32 |
+
-DCMAKE_ANDROID_NDK=/Users/friday/Library/Android/sdk/ndk/27.0.12077973 \
|
| 33 |
+
-DCMAKE_TOOLCHAIN_FILE=/Users/friday/Library/Android/sdk/ndk/27.0.12077973/build/cmake/android.toolchain.cmake \
|
| 34 |
+
-DCMAKE_MAKE_PROGRAM=/Users/friday/Library/Android/sdk/cmake/3.22.1/bin/ninja \
|
| 35 |
+
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=/Users/friday/premithus/build/llama_sdk/intermediates/cxx/Debug/1y5h19dr/obj/arm64-v8a \
|
| 36 |
+
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=/Users/friday/premithus/build/llama_sdk/intermediates/cxx/Debug/1y5h19dr/obj/arm64-v8a \
|
| 37 |
+
-DCMAKE_BUILD_TYPE=Debug \
|
| 38 |
+
-B/Users/friday/.pub-cache/git/lcpp-7c2f8423e87781859acc514cd0aa36554f423163/android/.cxx/Debug/1y5h19dr/arm64-v8a \
|
| 39 |
+
-GNinja
|
| 40 |
+
from /Users/friday/.pub-cache/git/lcpp-7c2f8423e87781859acc514cd0aa36554f423163/android
|
| 41 |
+
CMake Error at CMakeLists.txt:15 (add_subdirectory):
|
| 42 |
+
The source directory
|
| 43 |
+
|
| 44 |
+
/Users/friday/.pub-cache/git/lcpp-7c2f8423e87781859acc514cd0aa36554f423163/src/llama_cpp
|
| 45 |
+
|
| 46 |
+
does not contain a CMakeLists.txt file.
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
CMake Error at CMakeLists.txt:17 (target_include_directories):
|
| 50 |
+
Cannot specify include directories for target "llama" which is not built by
|
| 51 |
+
this project.
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
CMake Error at CMakeLists.txt:23 (target_sources):
|
| 55 |
+
Cannot specify sources for target "llama" which is not built by this
|
| 56 |
+
project.
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
CMake Error at CMakeLists.txt:30 (target_compile_options):
|
| 60 |
+
Cannot specify compile options for target "llama" which is not built by
|
| 61 |
+
this project.
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
CMake Error at CMakeLists.txt:39 (target_link_options):
|
| 65 |
+
Cannot specify link options for target "llama" which is not built by this
|
| 66 |
+
project.
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
CMake Error at CMakeLists.txt:49 (target_compile_options):
|
| 70 |
+
Cannot specify compile options for target "llama" which is not built by
|
| 71 |
+
this project. : com.android.ide.common.process.ProcessException: -- The C compiler identification is Clang 18.0.1
|
| 72 |
+
-- The CXX compiler identification is Clang 18.0.1
|
| 73 |
+
-- Detecting C compiler ABI info
|
| 74 |
+
-- Detecting C compiler ABI info - done
|
| 75 |
+
-- Check for working C compiler: /Users/friday/Library/Android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang - skipped
|
| 76 |
+
-- Detecting C compile features
|
| 77 |
+
-- Detecting C compile features - done
|
| 78 |
+
-- Detecting CXX compiler ABI info
|
| 79 |
+
-- Detecting CXX compiler ABI info - done
|
| 80 |
+
-- Check for working CXX compiler: /Users/friday/Library/Android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang++ - skipped
|
| 81 |
+
-- Detecting CXX compile features
|
| 82 |
+
-- Detecting CXX compile features - done
|
| 83 |
+
-- Configuring incomplete, errors occurred!
|
| 84 |
+
See also "/Users/friday/.pub-cache/git/lcpp-7c2f8423e87781859acc514cd0aa36554f423163/android/.cxx/Debug/1y5h19dr/arm64-v8a/CMakeFiles/CMakeOutput.log".
|
| 85 |
+
|
| 86 |
+
C++ build system [configure] failed while executing:
|
| 87 |
+
/Users/friday/Library/Android/sdk/cmake/3.22.1/bin/cmake \
|
| 88 |
+
-H/Users/friday/.pub-cache/git/lcpp-7c2f8423e87781859acc514cd0aa36554f423163/android \
|
| 89 |
+
-DCMAKE_SYSTEM_NAME=Android \
|
| 90 |
+
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
| 91 |
+
-DCMAKE_SYSTEM_VERSION=23 \
|
| 92 |
+
-DANDROID_PLATFORM=android-23 \
|
| 93 |
+
-DANDROID_ABI=arm64-v8a \
|
| 94 |
+
-DCMAKE_ANDROID_ARCH_ABI=arm64-v8a \
|
| 95 |
+
-DANDROID_NDK=/Users/friday/Library/Android/sdk/ndk/27.0.12077973 \
|
| 96 |
+
-DCMAKE_ANDROID_NDK=/Users/friday/Library/Android/sdk/ndk/27.0.12077973 \
|
| 97 |
+
-DCMAKE_TOOLCHAIN_FILE=/Users/friday/Library/Android/sdk/ndk/27.0.12077973/build/cmake/android.toolchain.cmake \
|
| 98 |
+
-DCMAKE_MAKE_PROGRAM=/Users/friday/Library/Android/sdk/cmake/3.22.1/bin/ninja \
|
| 99 |
+
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=/Users/friday/premithus/build/llama_sdk/intermediates/cxx/Debug/1y5h19dr/obj/arm64-v8a \
|
| 100 |
+
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY=/Users/friday/premithus/build/llama_sdk/intermediates/cxx/Debug/1y5h19dr/obj/arm64-v8a \
|
| 101 |
+
-DCMAKE_BUILD_TYPE=Debug \
|
| 102 |
+
-B/Users/friday/.pub-cache/git/lcpp-7c2f8423e87781859acc514cd0aa36554f423163/android/.cxx/Debug/1y5h19dr/arm64-v8a \
|
| 103 |
+
-GNinja
|
| 104 |
+
from /Users/friday/.pub-cache/git/lcpp-7c2f8423e87781859acc514cd0aa36554f423163/android
|
| 105 |
+
CMake Error at CMakeLists.txt:15 (add_subdirectory):
|
| 106 |
+
The source directory
|
| 107 |
+
|
| 108 |
+
/Users/friday/.pub-cache/git/lcpp-7c2f8423e87781859acc514cd0aa36554f423163/src/llama_cpp
|
| 109 |
+
|
| 110 |
+
does not contain a CMakeLists.txt file.
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
CMake Error at CMakeLists.txt:17 (target_include_directories):
|
| 114 |
+
Cannot specify include directories for target "llama" which is not built by
|
| 115 |
+
this project.
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
CMake Error at CMakeLists.txt:23 (target_sources):
|
| 119 |
+
Cannot specify sources for target "llama" which is not built by this
|
| 120 |
+
project.
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
CMake Error at CMakeLists.txt:30 (target_compile_options):
|
| 124 |
+
Cannot specify compile options for target "llama" which is not built by
|
| 125 |
+
this project.
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
CMake Error at CMakeLists.txt:39 (target_link_options):
|
| 129 |
+
Cannot specify link options for target "llama" which is not built by this
|
| 130 |
+
project.
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
CMake Error at CMakeLists.txt:49 (target_compile_options):
|
| 134 |
+
Cannot specify compile options for target "llama" which is not built by
|
| 135 |
+
this project.
|
| 136 |
+
at com.android.build.gradle.internal.cxx.process.ExecuteProcessKt.execute(ExecuteProcess.kt:288)
|
| 137 |
+
at com.android.build.gradle.internal.cxx.process.ExecuteProcessKt$executeProcess$1.invoke(ExecuteProcess.kt:108)
|
| 138 |
+
at com.android.build.gradle.internal.cxx.process.ExecuteProcessKt$executeProcess$1.invoke(ExecuteProcess.kt:106)
|
| 139 |
+
at com.android.build.gradle.internal.cxx.timing.TimingEnvironmentKt.time(TimingEnvironment.kt:32)
|
| 140 |
+
at com.android.build.gradle.internal.cxx.process.ExecuteProcessKt.executeProcess(ExecuteProcess.kt:106)
|
| 141 |
+
at com.android.build.gradle.internal.cxx.process.ExecuteProcessKt.executeProcess$default(ExecuteProcess.kt:85)
|
| 142 |
+
at com.android.build.gradle.tasks.CmakeQueryMetadataGenerator.executeProcess(CmakeFileApiMetadataGenerator.kt:59)
|
| 143 |
+
at com.android.build.gradle.tasks.ExternalNativeJsonGenerator$configureOneAbi$1$1$3.invoke(ExternalNativeJsonGenerator.kt:247)
|
| 144 |
+
at com.android.build.gradle.tasks.ExternalNativeJsonGenerator$configureOneAbi$1$1$3.invoke(ExternalNativeJsonGenerator.kt:247)
|
| 145 |
+
at com.android.build.gradle.internal.cxx.timing.TimingEnvironmentKt.time(TimingEnvironment.kt:32)
|
| 146 |
+
at com.android.build.gradle.tasks.ExternalNativeJsonGenerator.configureOneAbi(ExternalNativeJsonGenerator.kt:247)
|
| 147 |
+
at com.android.build.gradle.tasks.ExternalNativeJsonGenerator.configure(ExternalNativeJsonGenerator.kt:113)
|
| 148 |
+
at com.android.build.gradle.tasks.ExternalNativeBuildJsonTask.doTaskAction(ExternalNativeBuildJsonTask.kt:89)
|
| 149 |
+
at com.android.build.gradle.internal.tasks.UnsafeOutputsTask$taskAction$$inlined$recordTaskAction$1.invoke(BaseTask.kt:59)
|
| 150 |
+
at com.android.build.gradle.internal.tasks.Blocks.recordSpan(Blocks.java:51)
|
| 151 |
+
at com.android.build.gradle.internal.tasks.UnsafeOutputsTask.taskAction(UnsafeOutputsTask.kt:81)
|
| 152 |
+
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(Unknown Source)
|
| 153 |
+
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
|
| 154 |
+
at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:125)
|
| 155 |
+
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:58)
|
| 156 |
+
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:51)
|
| 157 |
+
at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:29)
|
| 158 |
+
at org.gradle.api.internal.tasks.execution.TaskExecution$3.run(TaskExecution.java:244)
|
| 159 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:30)
|
| 160 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:27)
|
| 161 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
|
| 162 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
|
| 163 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
|
| 164 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
|
| 165 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:48)
|
| 166 |
+
at org.gradle.api.internal.tasks.execution.TaskExecution.executeAction(TaskExecution.java:229)
|
| 167 |
+
at org.gradle.api.internal.tasks.execution.TaskExecution.executeActions(TaskExecution.java:212)
|
| 168 |
+
at org.gradle.api.internal.tasks.execution.TaskExecution.executeWithPreviousOutputFiles(TaskExecution.java:195)
|
| 169 |
+
at org.gradle.api.internal.tasks.execution.TaskExecution.execute(TaskExecution.java:162)
|
| 170 |
+
at org.gradle.internal.execution.steps.ExecuteStep.executeInternal(ExecuteStep.java:105)
|
| 171 |
+
at org.gradle.internal.execution.steps.ExecuteStep.access$000(ExecuteStep.java:44)
|
| 172 |
+
at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:59)
|
| 173 |
+
at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:56)
|
| 174 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:210)
|
| 175 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:205)
|
| 176 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
|
| 177 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
|
| 178 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
|
| 179 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
|
| 180 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:54)
|
| 181 |
+
at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:56)
|
| 182 |
+
at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:44)
|
| 183 |
+
at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:42)
|
| 184 |
+
at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:75)
|
| 185 |
+
at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:55)
|
| 186 |
+
at org.gradle.internal.execution.steps.PreCreateOutputParentsStep.execute(PreCreateOutputParentsStep.java:50)
|
| 187 |
+
at org.gradle.internal.execution.steps.PreCreateOutputParentsStep.execute(PreCreateOutputParentsStep.java:28)
|
| 188 |
+
at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:67)
|
| 189 |
+
at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:37)
|
| 190 |
+
at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:61)
|
| 191 |
+
at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:26)
|
| 192 |
+
at org.gradle.internal.execution.steps.CaptureOutputsAfterExecutionStep.execute(CaptureOutputsAfterExecutionStep.java:69)
|
| 193 |
+
at org.gradle.internal.execution.steps.CaptureOutputsAfterExecutionStep.execute(CaptureOutputsAfterExecutionStep.java:46)
|
| 194 |
+
at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:40)
|
| 195 |
+
at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:29)
|
| 196 |
+
at org.gradle.internal.execution.steps.BuildCacheStep.executeWithoutCache(BuildCacheStep.java:189)
|
| 197 |
+
at org.gradle.internal.execution.steps.BuildCacheStep.lambda$execute$1(BuildCacheStep.java:75)
|
| 198 |
+
at org.gradle.internal.Either$Right.fold(Either.java:175)
|
| 199 |
+
at org.gradle.internal.execution.caching.CachingState.fold(CachingState.java:62)
|
| 200 |
+
at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:73)
|
| 201 |
+
at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:48)
|
| 202 |
+
at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:46)
|
| 203 |
+
at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:35)
|
| 204 |
+
at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:75)
|
| 205 |
+
at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$2(SkipUpToDateStep.java:53)
|
| 206 |
+
at java.base/java.util.Optional.orElseGet(Unknown Source)
|
| 207 |
+
at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:53)
|
| 208 |
+
at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:35)
|
| 209 |
+
at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:37)
|
| 210 |
+
at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:27)
|
| 211 |
+
at org.gradle.internal.execution.steps.ResolveIncrementalCachingStateStep.executeDelegate(ResolveIncrementalCachingStateStep.java:49)
|
| 212 |
+
at org.gradle.internal.execution.steps.ResolveIncrementalCachingStateStep.executeDelegate(ResolveIncrementalCachingStateStep.java:27)
|
| 213 |
+
at org.gradle.internal.execution.steps.AbstractResolveCachingStateStep.execute(AbstractResolveCachingStateStep.java:71)
|
| 214 |
+
at org.gradle.internal.execution.steps.AbstractResolveCachingStateStep.execute(AbstractResolveCachingStateStep.java:39)
|
| 215 |
+
at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:65)
|
| 216 |
+
at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:36)
|
| 217 |
+
at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:107)
|
| 218 |
+
at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:56)
|
| 219 |
+
at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.execute(AbstractCaptureStateBeforeExecutionStep.java:64)
|
| 220 |
+
at org.gradle.internal.execution.steps.AbstractCaptureStateBeforeExecutionStep.execute(AbstractCaptureStateBeforeExecutionStep.java:43)
|
| 221 |
+
at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.executeWithNonEmptySources(AbstractSkipEmptyWorkStep.java:125)
|
| 222 |
+
at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.execute(AbstractSkipEmptyWorkStep.java:56)
|
| 223 |
+
at org.gradle.internal.execution.steps.AbstractSkipEmptyWorkStep.execute(AbstractSkipEmptyWorkStep.java:36)
|
| 224 |
+
at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:38)
|
| 225 |
+
at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:36)
|
| 226 |
+
at org.gradle.internal.execution.steps.LoadPreviousExecutionStateStep.execute(LoadPreviousExecutionStateStep.java:23)
|
| 227 |
+
at org.gradle.internal.execution.steps.HandleStaleOutputsStep.execute(HandleStaleOutputsStep.java:75)
|
| 228 |
+
at org.gradle.internal.execution.steps.HandleStaleOutputsStep.execute(HandleStaleOutputsStep.java:41)
|
| 229 |
+
at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.lambda$execute$0(AssignMutableWorkspaceStep.java:35)
|
| 230 |
+
at org.gradle.api.internal.tasks.execution.TaskExecution$4.withWorkspace(TaskExecution.java:289)
|
| 231 |
+
at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.execute(AssignMutableWorkspaceStep.java:31)
|
| 232 |
+
at org.gradle.internal.execution.steps.AssignMutableWorkspaceStep.execute(AssignMutableWorkspaceStep.java:22)
|
| 233 |
+
at org.gradle.internal.execution.steps.ChoosePipelineStep.execute(ChoosePipelineStep.java:40)
|
| 234 |
+
at org.gradle.internal.execution.steps.ChoosePipelineStep.execute(ChoosePipelineStep.java:23)
|
| 235 |
+
at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.lambda$execute$2(ExecuteWorkBuildOperationFiringStep.java:67)
|
| 236 |
+
at java.base/java.util.Optional.orElseGet(Unknown Source)
|
| 237 |
+
at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.execute(ExecuteWorkBuildOperationFiringStep.java:67)
|
| 238 |
+
at org.gradle.internal.execution.steps.ExecuteWorkBuildOperationFiringStep.execute(ExecuteWorkBuildOperationFiringStep.java:39)
|
| 239 |
+
at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:46)
|
| 240 |
+
at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:34)
|
| 241 |
+
at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:48)
|
| 242 |
+
at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:35)
|
| 243 |
+
at org.gradle.internal.execution.impl.DefaultExecutionEngine$1.execute(DefaultExecutionEngine.java:64)
|
| 244 |
+
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:127)
|
| 245 |
+
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:116)
|
| 246 |
+
at org.gradle.api.internal.tasks.execution.ProblemsTaskPathTrackingTaskExecuter.execute(ProblemsTaskPathTrackingTaskExecuter.java:41)
|
| 247 |
+
at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
|
| 248 |
+
at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
|
| 249 |
+
at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
|
| 250 |
+
at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:74)
|
| 251 |
+
at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
|
| 252 |
+
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
|
| 253 |
+
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
|
| 254 |
+
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
|
| 255 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:210)
|
| 256 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:205)
|
| 257 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
|
| 258 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
|
| 259 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
|
| 260 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
|
| 261 |
+
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:54)
|
| 262 |
+
at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
|
| 263 |
+
at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:42)
|
| 264 |
+
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:331)
|
| 265 |
+
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:318)
|
| 266 |
+
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.lambda$execute$0(DefaultTaskExecutionGraph.java:314)
|
| 267 |
+
at org.gradle.internal.operations.CurrentBuildOperationRef.with(CurrentBuildOperationRef.java:85)
|
| 268 |
+
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:314)
|
| 269 |
+
at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:303)
|
| 270 |
+
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:459)
|
| 271 |
+
at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:376)
|
| 272 |
+
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
|
| 273 |
+
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:48)
|
| 274 |
+
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
|
| 275 |
+
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
|
| 276 |
+
at java.base/java.lang.Thread.run(Unknown Source)
|
| 277 |
+
Caused by: com.android.ide.common.process.ProcessException: Error while executing process /Users/friday/Library/Android/sdk/cmake/3.22.1/bin/cmake with arguments {-H/Users/friday/.pub-cache/git/lcpp-7c2f8423e87781859acc514cd0aa36554f423163/android -DCMAKE_SYSTEM_NAME=Android -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DCMAKE_SYSTEM_VERSION=23 -DANDROID_PLATFORM=android-23 -DANDROID_ABI=arm64-v8a -DCMAKE_ANDROID_ARCH_ABI=arm64-v8a -DANDROID_NDK=/Users/friday/Library/Android/sdk/ndk/27.0.12077973 -DCMAKE_ANDROID_NDK=/Users/friday/Library/Android/sdk/ndk/27.0.12077973 -DCMAKE_TOOLCHAIN_FILE=/Users/friday/Library/Android/sdk/ndk/27.0.12077973/build/cmake/android.toolchain.cmake -DCMAKE_MAKE_PROGRAM=/Users/friday/Library/Android/sdk/cmake/3.22.1/bin/ninja -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=/Users/friday/premithus/build/llama_sdk/intermediates/cxx/Debug/1y5h19dr/obj/arm64-v8a -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=/Users/friday/premithus/build/llama_sdk/intermediates/cxx/Debug/1y5h19dr/obj/arm64-v8a -DCMAKE_BUILD_TYPE=Debug -B/Users/friday/.pub-cache/git/lcpp-7c2f8423e87781859acc514cd0aa36554f423163/android/.cxx/Debug/1y5h19dr/arm64-v8a -GNinja}
|
| 278 |
+
at com.android.build.gradle.internal.process.GradleProcessResult.buildProcessException(GradleProcessResult.java:73)
|
| 279 |
+
at com.android.build.gradle.internal.process.GradleProcessResult.assertNormalExitValue(GradleProcessResult.java:48)
|
| 280 |
+
at com.android.build.gradle.internal.cxx.process.ExecuteProcessKt.execute(ExecuteProcess.kt:277)
|
| 281 |
+
... 140 more
|
| 282 |
+
Caused by: org.gradle.process.internal.ExecException: Process 'command '/Users/friday/Library/Android/sdk/cmake/3.22.1/bin/cmake'' finished with non-zero exit value 1
|
| 283 |
+
at org.gradle.process.internal.DefaultExecHandle$ExecResultImpl.assertNormalExitValue(DefaultExecHandle.java:442)
|
| 284 |
+
at com.android.build.gradle.internal.process.GradleProcessResult.assertNormalExitValue(GradleProcessResult.java:46)
|
| 285 |
+
... 141 more
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
* Try:
|
| 289 |
+
> Run with --stacktrace option to get the stack trace.
|
| 290 |
+
> Run with --info or --debug option to get more log output.
|
| 291 |
+
> Run with --scan to get full insights.
|
| 292 |
+
> Get more help at https://help.gradle.org.
|
| 293 |
+
|
| 294 |
+
BUILD FAILED in 2s
|
| 295 |
+
Running Gradle task 'assembleDebug'... 2,115ms
|
| 296 |
+
Gradle task assembleDebug failed with exit code 1
|
convert_bert.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import tensorflow as tf
|
| 2 |
+
from transformers import AutoTokenizer, TFAutoModelForSequenceClassification
|
| 3 |
+
import os
|
| 4 |
+
|
| 5 |
+
model_name = "mrm8488/bert-tiny-finetuned-sms-spam-detection"
|
| 6 |
+
|
| 7 |
+
print("Loading model and tokenizer...")
|
| 8 |
+
tokenizer = AutoTokenizer.from_pretrained(model_name)
|
| 9 |
+
model = TFAutoModelForSequenceClassification.from_pretrained(model_name, from_pt=True)
|
| 10 |
+
|
| 11 |
+
print("Exporting vocab...")
|
| 12 |
+
os.makedirs("models", exist_ok=True)
|
| 13 |
+
with open("models/vocab.txt", "w", encoding="utf-8") as f:
|
| 14 |
+
for token, index in sorted(tokenizer.get_vocab().items(), key=lambda x: x[1]):
|
| 15 |
+
f.write(f"{token}\n")
|
| 16 |
+
|
| 17 |
+
print("Tracing model for TFLite conversion...")
|
| 18 |
+
MAX_SEQ_LEN = 128
|
| 19 |
+
|
| 20 |
+
callable_model = tf.function(
|
| 21 |
+
lambda input_ids, attention_mask: model(input_ids=input_ids, attention_mask=attention_mask, training=False)
|
| 22 |
+
)
|
| 23 |
+
|
| 24 |
+
concrete_func = callable_model.get_concrete_function(
|
| 25 |
+
tf.TensorSpec([1, MAX_SEQ_LEN], tf.int32, name="input_ids"),
|
| 26 |
+
tf.TensorSpec([1, MAX_SEQ_LEN], tf.int32, name="attention_mask")
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
print("Converting to TFLite...")
|
| 30 |
+
converter = tf.lite.TFLiteConverter.from_concrete_functions([concrete_func])
|
| 31 |
+
converter.optimizations = [tf.lite.Optimize.DEFAULT]
|
| 32 |
+
converter.target_spec.supported_ops = [
|
| 33 |
+
tf.lite.OpsSet.TFLITE_BUILTINS,
|
| 34 |
+
tf.lite.OpsSet.SELECT_TF_OPS
|
| 35 |
+
]
|
| 36 |
+
|
| 37 |
+
tflite_model = converter.convert()
|
| 38 |
+
|
| 39 |
+
with open("models/scam_classifier.tflite", "wb") as f:
|
| 40 |
+
f.write(tflite_model)
|
| 41 |
+
|
| 42 |
+
print("Done! Model saved to models/scam_classifier.tflite")
|
demo_script_v0.0.2.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Premithius V0.0.2 Demo Script
|
| 2 |
+
|
| 3 |
+
**Theme:** India-First On-Device AI Security | Zero LLM | ARMadillo Architecture
|
| 4 |
+
|
| 5 |
+
## Demo 1: The Zero-Day Scam (Vector Engine)
|
| 6 |
+
**Scenario:** A completely new scam variant that isn't in any blacklist yet.
|
| 7 |
+
1. Send SMS to emulator: `adb shell am broadcast -a android.provider.Telephony.SMS_RECEIVED -d "SMS_BODY:Urgent! Your HDFC acct is blocked. Update KYC immediately via http://hdfc.kyc-update-xyz.in"`
|
| 8 |
+
2. **Observe:** Premithius overlay appears instantly (under 50ms).
|
| 9 |
+
3. **Explanation:** "Unlike cloud antiviruses that need updates, our `sqlite-vec` engine running locally instantly recognized the semantic meaning of 'urgent KYC update' bypassing traditional filters. It never left the device."
|
| 10 |
+
|
| 11 |
+
## Demo 2: The UPI Rs. 1 Collect Fraud (India Threat Pack)
|
| 12 |
+
**Scenario:** The classic OLX/Marketplace fraud.
|
| 13 |
+
1. Send SMS: `adb shell am broadcast -a android.provider.Telephony.SMS_RECEIVED -d "SMS_BODY:Dear Customer, Rs 12,500 has been credited... To receive money, please authorize Rs. 1 collect request from VPA: scammer@ybl"`
|
| 14 |
+
2. **Observe:** Kavacha Firewall blocks it natively.
|
| 15 |
+
3. **Explanation:** "Our Layer 1 India Threat Pack specifically targets regional payment infrastructure. We catch the ₹1 collect trick natively before it hits complex models."
|
| 16 |
+
|
| 17 |
+
## Demo 3: Data Shield & System Visibility
|
| 18 |
+
1. Open the Premithius App -> Navigate to **Data Shield Insights**
|
| 19 |
+
2. **Observe:** Plain English translations of complex OS data (e.g., "TikTok sent 340 MB of data between 2am–4am last night").
|
| 20 |
+
3. **Explanation:** "Instead of obscure permission screens, we translate system telemetry into human-readable privacy stories. The home screen widget surfaces these anomalies effortlessly."
|
| 21 |
+
|
| 22 |
+
## Demo 4: Aadhaar/PAN Outgoing Intercept (Keyboard)
|
| 23 |
+
**Scenario:** User typing their Aadhaar number to a stranger on WhatsApp.
|
| 24 |
+
1. Open any chat app.
|
| 25 |
+
2. Select Premithius Keyboard.
|
| 26 |
+
3. Type: `Mera Aadhaar 1234 5678 9012 hai` and hit Enter/Send.
|
| 27 |
+
4. **Observe:** Warning overlay/snack bar: "Wait! You are about to send a sensitive ID."
|
| 28 |
+
5. **Explanation:** "Our keyboard is the final line of defense. It detects outbound PII like Aadhaar or PAN on the edge, warning the user *before* it ends up on a scammer's server."
|
hf_space
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
Subproject commit ca0c577efb6417cf89c554b17f3c9b8af9a3fa42
|
index.html
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!doctype html>
|
| 2 |
+
<html>
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="utf-8" />
|
| 5 |
+
<meta name="viewport" content="width=device-width" />
|
| 6 |
+
<title>My static Space</title>
|
| 7 |
+
<link rel="stylesheet" href="style.css" />
|
| 8 |
+
</head>
|
| 9 |
+
<body>
|
| 10 |
+
<div class="card">
|
| 11 |
+
<h1>Welcome to your static Space!</h1>
|
| 12 |
+
<p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
|
| 13 |
+
<p>
|
| 14 |
+
Also don't forget to check the
|
| 15 |
+
<a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
|
| 16 |
+
</p>
|
| 17 |
+
</div>
|
| 18 |
+
</body>
|
| 19 |
+
</html>
|
ios/.gitignore
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
**/dgph
|
| 2 |
+
*.mode1v3
|
| 3 |
+
*.mode2v3
|
| 4 |
+
*.moved-aside
|
| 5 |
+
*.pbxuser
|
| 6 |
+
*.perspectivev3
|
| 7 |
+
**/*sync/
|
| 8 |
+
.sconsign.dblite
|
| 9 |
+
.tags*
|
| 10 |
+
**/.vagrant/
|
| 11 |
+
**/DerivedData/
|
| 12 |
+
Icon?
|
| 13 |
+
**/Pods/
|
| 14 |
+
**/.symlinks/
|
| 15 |
+
profile
|
| 16 |
+
xcuserdata
|
| 17 |
+
**/.generated/
|
| 18 |
+
Flutter/App.framework
|
| 19 |
+
Flutter/Flutter.framework
|
| 20 |
+
Flutter/Flutter.podspec
|
| 21 |
+
Flutter/Generated.xcconfig
|
| 22 |
+
Flutter/ephemeral/
|
| 23 |
+
Flutter/app.flx
|
| 24 |
+
Flutter/app.zip
|
| 25 |
+
Flutter/flutter_assets/
|
| 26 |
+
Flutter/flutter_export_environment.sh
|
| 27 |
+
ServiceDefinitions.json
|
| 28 |
+
Runner/GeneratedPluginRegistrant.*
|
| 29 |
+
|
| 30 |
+
# Exceptions to above rules.
|
| 31 |
+
!default.mode1v3
|
| 32 |
+
!default.mode2v3
|
| 33 |
+
!default.pbxuser
|
| 34 |
+
!default.perspectivev3
|
ios/Flutter/AppFrameworkInfo.plist
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
| 3 |
+
<plist version="1.0">
|
| 4 |
+
<dict>
|
| 5 |
+
<key>CFBundleDevelopmentRegion</key>
|
| 6 |
+
<string>en</string>
|
| 7 |
+
<key>CFBundleExecutable</key>
|
| 8 |
+
<string>App</string>
|
| 9 |
+
<key>CFBundleIdentifier</key>
|
| 10 |
+
<string>io.flutter.flutter.app</string>
|
| 11 |
+
<key>CFBundleInfoDictionaryVersion</key>
|
| 12 |
+
<string>6.0</string>
|
| 13 |
+
<key>CFBundleName</key>
|
| 14 |
+
<string>App</string>
|
| 15 |
+
<key>CFBundlePackageType</key>
|
| 16 |
+
<string>FMWK</string>
|
| 17 |
+
<key>CFBundleShortVersionString</key>
|
| 18 |
+
<string>1.0</string>
|
| 19 |
+
<key>CFBundleSignature</key>
|
| 20 |
+
<string>????</string>
|
| 21 |
+
<key>CFBundleVersion</key>
|
| 22 |
+
<string>1.0</string>
|
| 23 |
+
</dict>
|
| 24 |
+
</plist>
|
ios/Flutter/Debug.xcconfig
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
| 2 |
+
#include "Generated.xcconfig"
|