From e7792474667517f9c514453e3fbefff270c6ae0a Mon Sep 17 00:00:00 2001 From: alex5402 Date: Fri, 30 Jan 2026 18:24:57 +0530 Subject: [PATCH] feat: Introduce black-reflection and compiler modules, integrate reflection utilities, and update core components. --- .gitignore | 1 + ADDITIONAL_CRASH_FIXES.md | 477 --------------- APK_PATH_FIX.md | 258 --------- APK_PATH_VALIDATION_FIX.md | 267 --------- Bcore/build.gradle | 19 +- .../top/niunaijun/blackbox/BlackBoxCore.java | 109 +++- .../configuration/AppLifecycleCallback.java | 14 + .../blackbox/fake/hook/HookManager.java | 5 + .../fake/service/BrowserEngineProxy.java | 94 +++ .../fake/service/IAppOpsManagerProxy.java | 106 ++-- .../fake/service/IAttributionSourceProxy.java | 12 +- ...ensitiveContentProtectionManagerProxy.java | 75 +++ .../providers/ContentProviderStub.java | 60 +- .../context/providers/SystemProviderStub.java | 20 +- .../utils/AttributionSourceUtils.java | 6 +- .../utils/StoragePermissionHelper.java | 217 +++++++ .../blackbox/utils/compat/ContextCompat.java | 4 +- CHANGES_SUMMARY.md | 178 ------ COMPILATION_ERRORS_FIXED.md | 146 ----- COMPILATION_ERROR_FIX.md | 111 ---- COMPILATION_FIX_SUMMARY.md | 69 --- COMPREHENSIVE_CRASH_FIXES_SUMMARY.md | 380 ------------ CONTEXT_CREATION_FIX.md | 317 ---------- DAEMON_SERVICE_FIXES.md | 541 ------------------ DEX_FILE_CORRUPTION_FIXES.md | 396 ------------- DNS_INTERNET_ACCESS_FIX.md | 152 ----- FREEZING_BUG_FIXES.md | 173 ------ INFINITE_RECURSION_FIX.md | 193 ------- JAR_IMPROVEMENTS.md | 234 -------- NULLPOINTER_EXCEPTION_FIX.md | 221 ------- REAL_WORLD_APK_PATH_FIX.md | 258 --------- RELEASE_NOTES.md | 421 ++++---------- ReleaseNotes.md | 203 ------- SECURITY_CRASH_FIXES.md | 239 -------- SOCIAL_MEDIA_CRASH_FIXES.md | 308 ---------- SYNTAX_ERROR_FIX.md | 155 ----- XIAOMI_CRASH_FIXES.md | 313 ---------- .../blackboxa/view/main/BlackBoxLoader.kt | 20 + .../blackboxa/view/main/MainActivity.kt | 156 +++-- black-reflection/.gitignore | 1 + black-reflection/build.gradle.kts | 7 + .../BlackNullPointerException.java | 15 + .../blackreflection/BlackReflection.java | 245 ++++++++ .../blackreflection/annotation/BClass.java | 14 + .../annotation/BClassName.java | 14 + .../annotation/BClassNameNotProcess.java | 14 + .../annotation/BConstructor.java | 21 + .../annotation/BConstructorNotProcess.java | 21 + .../blackreflection/annotation/BField.java | 14 + .../annotation/BFieldCheckNotProcess.java | 13 + .../annotation/BFieldNotProcess.java | 15 + .../annotation/BFieldSetNotProcess.java | 13 + .../blackreflection/annotation/BMethod.java | 13 + .../annotation/BMethodCheckNotProcess.java | 13 + .../annotation/BParamClass.java | 15 + .../annotation/BParamClassName.java | 14 + .../annotation/BStaticField.java | 13 + .../annotation/BStaticMethod.java | 13 + .../blackreflection/utils/ClassUtil.java | 39 ++ .../blackreflection/utils/Reflector.java | 440 ++++++++++++++ build.gradle | 9 +- compiler/.gitignore | 1 + compiler/build.gradle.kts | 15 + .../blackreflection/BlackReflectionInfo.java | 25 + .../BlackReflectionInterfaceInfo.java | 27 + .../BlackReflectionProcessor.java | 190 ++++++ .../proxy/BlackReflectionInterfaceProxy.java | 202 +++++++ .../proxy/BlackReflectionProxy.java | 97 ++++ .../blackreflection/utils/ClassUtils.java | 30 + settings.gradle | 2 + 70 files changed, 2424 insertions(+), 6069 deletions(-) delete mode 100644 ADDITIONAL_CRASH_FIXES.md delete mode 100644 APK_PATH_FIX.md delete mode 100644 APK_PATH_VALIDATION_FIX.md create mode 100644 Bcore/src/main/java/top/niunaijun/blackbox/fake/service/BrowserEngineProxy.java create mode 100644 Bcore/src/main/java/top/niunaijun/blackbox/fake/service/ISensitiveContentProtectionManagerProxy.java create mode 100644 Bcore/src/main/java/top/niunaijun/blackbox/utils/StoragePermissionHelper.java delete mode 100644 CHANGES_SUMMARY.md delete mode 100644 COMPILATION_ERRORS_FIXED.md delete mode 100644 COMPILATION_ERROR_FIX.md delete mode 100644 COMPILATION_FIX_SUMMARY.md delete mode 100644 COMPREHENSIVE_CRASH_FIXES_SUMMARY.md delete mode 100644 CONTEXT_CREATION_FIX.md delete mode 100644 DAEMON_SERVICE_FIXES.md delete mode 100644 DEX_FILE_CORRUPTION_FIXES.md delete mode 100644 DNS_INTERNET_ACCESS_FIX.md delete mode 100644 FREEZING_BUG_FIXES.md delete mode 100644 INFINITE_RECURSION_FIX.md delete mode 100644 JAR_IMPROVEMENTS.md delete mode 100644 NULLPOINTER_EXCEPTION_FIX.md delete mode 100644 REAL_WORLD_APK_PATH_FIX.md delete mode 100644 ReleaseNotes.md delete mode 100644 SECURITY_CRASH_FIXES.md delete mode 100644 SOCIAL_MEDIA_CRASH_FIXES.md delete mode 100644 SYNTAX_ERROR_FIX.md delete mode 100644 XIAOMI_CRASH_FIXES.md create mode 100644 black-reflection/.gitignore create mode 100644 black-reflection/build.gradle.kts create mode 100644 black-reflection/src/main/java/top/niunaijun/blackreflection/BlackNullPointerException.java create mode 100644 black-reflection/src/main/java/top/niunaijun/blackreflection/BlackReflection.java create mode 100644 black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BClass.java create mode 100644 black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BClassName.java create mode 100644 black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BClassNameNotProcess.java create mode 100644 black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BConstructor.java create mode 100644 black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BConstructorNotProcess.java create mode 100644 black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BField.java create mode 100644 black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BFieldCheckNotProcess.java create mode 100644 black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BFieldNotProcess.java create mode 100644 black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BFieldSetNotProcess.java create mode 100644 black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BMethod.java create mode 100644 black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BMethodCheckNotProcess.java create mode 100644 black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BParamClass.java create mode 100644 black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BParamClassName.java create mode 100644 black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BStaticField.java create mode 100644 black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BStaticMethod.java create mode 100644 black-reflection/src/main/java/top/niunaijun/blackreflection/utils/ClassUtil.java create mode 100644 black-reflection/src/main/java/top/niunaijun/blackreflection/utils/Reflector.java create mode 100644 compiler/.gitignore create mode 100644 compiler/build.gradle.kts create mode 100644 compiler/src/main/java/top/niunaijun/blackreflection/BlackReflectionInfo.java create mode 100644 compiler/src/main/java/top/niunaijun/blackreflection/BlackReflectionInterfaceInfo.java create mode 100644 compiler/src/main/java/top/niunaijun/blackreflection/BlackReflectionProcessor.java create mode 100644 compiler/src/main/java/top/niunaijun/blackreflection/proxy/BlackReflectionInterfaceProxy.java create mode 100644 compiler/src/main/java/top/niunaijun/blackreflection/proxy/BlackReflectionProxy.java create mode 100644 compiler/src/main/java/top/niunaijun/blackreflection/utils/ClassUtils.java diff --git a/.gitignore b/.gitignore index 52578fa..43fcf7b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ .gradle /logs/ /local.properties +/inspiration /.idea/caches /.idea/libraries /.idea/modules.xml diff --git a/ADDITIONAL_CRASH_FIXES.md b/ADDITIONAL_CRASH_FIXES.md deleted file mode 100644 index 4720e61..0000000 --- a/ADDITIONAL_CRASH_FIXES.md +++ /dev/null @@ -1,477 +0,0 @@ -# Additional Crash Fixes for Bcore - -## Overview - -Since the initial DEX file corruption fixes were implemented but apps are still crashing, this document describes the **additional comprehensive crash prevention systems** that have been added to handle all types of crashes including native crashes, memory issues, and system-level failures. - -## Current Status - -The initial fixes addressed: -✅ **DEX File Corruption** - ClassNotFoundException and "classes.dex: Entry not found" errors -✅ **WebView Data Directory Conflicts** - WebView initialization failures -✅ **AttributionSource UID Issues** - Android 12+ security enforcement problems - -However, apps are still crashing, indicating additional issues that need to be addressed. - -## Additional Crash Prevention Systems - -### 1. Native Crash Prevention - -**File**: `NativeCrashPrevention.java` - -#### Purpose -Handle native-level crashes like SIGSEGV, SIGABRT, and other system-level failures that were seen in the logs. - -#### Key Features -- **Signal Handler Installation** - Catches native crash signals -- **Native Library Monitoring** - Monitors problematic native libraries (libart.so, libc.so, etc.) -- **Memory Protection** - Monitors memory usage and prevents memory-related crashes -- **Thread Protection** - Monitors thread health and prevents thread-related crashes - -#### Recovery Strategies -1. **Thread Restart** - Attempts to restart crashed threads -2. **Native Cache Clearing** - Clears corrupted native caches -3. **Library Reinitialization** - Reinitializes corrupted native libraries -4. **Memory Cleanup** - Performs aggressive memory cleanup and garbage collection - -#### Implementation Details -```java -public static void initialize() { - // Install signal handlers - installSignalHandlers(); - - // Install native library monitoring - installNativeLibraryMonitoring(); - - // Install memory protection - installMemoryProtection(); - - // Install thread protection - installThreadProtection(); -} -``` - -### 2. Comprehensive Crash Monitoring - -**File**: `CrashMonitor.java` - -#### Purpose -Monitor, detect, and automatically recover from ALL types of crashes in real-time. - -#### Key Features -- **Real-time Crash Detection** - Detects crashes as they happen -- **Automatic Recovery** - Attempts recovery using multiple strategies -- **Crash Pattern Analysis** - Identifies problematic patterns and trends -- **Health Monitoring** - Continuous system health checks -- **Crash Logging** - Detailed crash logs for analysis - -#### Recovery Strategies -1. **Java Exception Recovery** - Handles Java-level exceptions -2. **Native Crash Recovery** - Handles native crashes -3. **DEX Corruption Recovery** - Handles DEX file issues -4. **WebView Crash Recovery** - Handles WebView failures -5. **Memory Crash Recovery** - Handles memory-related crashes - -#### Monitoring Features -```java -// Periodic health checks every 30 seconds -startPeriodicHealthChecks(); - -// Crash pattern analysis every 60 seconds -startCrashPatternAnalysis(); - -// Real-time crash statistics -getCrashStats(); -``` - -### 3. Enhanced System Integration - -#### BlackBoxCore Integration -All crash prevention systems are now automatically installed at startup: - -```java -static { - try { - // Install all crash prevention mechanisms at class loading time - SimpleCrashFix.installSimpleFix(); - StackTraceFilter.install(); - SocialMediaAppCrashPrevention.initialize(); - DexCrashPrevention.initialize(); - NativeCrashPrevention.initialize(); // NEW: Native crash prevention - CrashMonitor.initialize(); // NEW: Comprehensive monitoring - } catch (Exception e) { - Slog.w(TAG, "Failed to install crash prevention: " + e.getMessage()); - } -} -``` - -## Crash Prevention Architecture - -### Multi-Layer Protection System - -``` -┌─────────────────────────────────────────────────────────────┐ -│ Application Layer │ -├─────────────────────────────────────────────────────────────┤ -│ Social Media App Protection │ -├─────────────────────────────────────────────────────────────┤ -│ WebView Protection │ -├─────────────────────────────────────────────────────────────┤ -│ AttributionSource Fixes │ -├─────────────────────────────────────────────────────────────┤ -│ DEX File Protection │ -├─────────────────────────────────────────────────────────────┤ -│ Native Crash Prevention │ -├─────────────────────────────────────────────────────────────┤ -│ Comprehensive Monitoring │ -├─────────────────────────────────────────────────────────────┤ -│ System Layer │ -└─────────────────────────────────────────────────────────────┘ -``` - -### Recovery Flow - -1. **Detection Phase** - - Crash detected by appropriate prevention system - - Crash information logged and categorized - - Recovery strategy selected based on crash type - -2. **Recovery Phase** - - Primary recovery strategy attempted - - Fallback strategies tried if primary fails - - System state restored to stable condition - -3. **Prevention Phase** - - Crash patterns analyzed - - Proactive measures implemented - - System health monitored continuously - -## Native Crash Handling - -### Signal Types Handled -- **SIGSEGV** - Segmentation violation (memory access errors) -- **SIGABRT** - Abort signal (program termination) -- **SIGBUS** - Bus error (memory alignment issues) -- **SIGFPE** - Floating point exception -- **SIGILL** - Illegal instruction - -### Recovery Mechanisms -```java -private static boolean attemptNativeCrashRecovery(Thread thread, Throwable throwable) { - // Strategy 1: Restart the crashed thread - if (restartCrashedThread(thread)) return true; - - // Strategy 2: Clear native caches - if (clearNativeCaches()) return true; - - // Strategy 3: Reinitialize native libraries - if (reinitializeNativeLibraries()) return true; - - // Strategy 4: Memory cleanup - if (performMemoryCleanup()) return true; - - return false; // All strategies failed -} -``` - -## Memory Management - -### Memory Protection Features -- **Automatic Garbage Collection** - Forces GC when memory usage > 80% -- **Memory Usage Monitoring** - Real-time memory usage tracking -- **Corrupted Property Cleanup** - Removes corrupted system properties -- **Cache Management** - Clears corrupted caches automatically - -### Memory Cleanup Process -```java -private static boolean performMemoryCleanup() { - // Force garbage collection multiple times - for (int i = 0; i < 3; i++) { - System.gc(); - Thread.sleep(100); - } - - // Clear corrupted system properties - clearCorruptedSystemProperties(); - - return true; -} -``` - -## Thread Management - -### Thread Protection Features -- **Thread Health Monitoring** - Monitors thread count and health -- **Thread Restart Capability** - Attempts to restart crashed threads -- **Thread Group Analysis** - Analyzes thread group hierarchy -- **Automatic Thread Recovery** - Recovers from thread-related crashes - -### Thread Monitoring -```java -private static void setupThreadMonitoring() { - ThreadGroup rootGroup = Thread.currentThread().getThreadGroup(); - while (rootGroup.getParent() != null) { - rootGroup = rootGroup.getParent(); - } - - int threadCount = rootGroup.activeCount(); - if (threadCount > 100) { - Slog.w(TAG, "High thread count detected: " + threadCount); - } -} -``` - -## Crash Pattern Analysis - -### Pattern Detection -- **Crash Type Analysis** - Groups crashes by type (Java, Native, DEX, etc.) -- **Package Analysis** - Identifies problematic apps -- **Frequency Analysis** - Detects high crash rates -- **Trend Analysis** - Identifies worsening crash patterns - -### Analysis Features -```java -private static void analyzeCrashPatterns() { - // Count crashes by type - Map crashesByType = new HashMap<>(); - - // Count crashes by package - Map crashesByPackage = new HashMap<>(); - - // Check for problematic patterns - for (Map.Entry entry : crashesByType.entrySet()) { - if (entry.getValue() > 5) { - Slog.w(TAG, "High crash rate detected for type: " + entry.getKey()); - } - } -} -``` - -## Health Monitoring - -### Periodic Health Checks -- **Memory Usage Monitoring** - Every 30 seconds -- **Thread Count Monitoring** - Every 30 seconds -- **System Property Validation** - Every 30 seconds -- **Cache Health Checks** - Every 30 seconds - -### Health Check Process -```java -private static void performHealthCheck() { - // Check memory usage - Runtime runtime = Runtime.getRuntime(); - double memoryUsagePercent = calculateMemoryUsage(runtime); - - if (memoryUsagePercent > 80) { - Slog.w(TAG, "High memory usage detected: " + memoryUsagePercent + "%"); - System.gc(); // Force garbage collection - } - - // Check thread count - int threadCount = getActiveThreadCount(); - if (threadCount > 100) { - Slog.w(TAG, "High thread count detected: " + threadCount); - } -} -``` - -## Crash Logging and Analysis - -### Log File Structure -``` -=== CRASH LOG === -Timestamp: 2025-08-17 23:11:10 -Crash Type: NativeCrash -Package: com.instagram.android -Error: SIGSEGV in libart.so -Recovered: true -=== STACK TRACE === -[Detailed stack trace] -=== END === -``` - -### Log Management -- **Automatic Log Creation** - Creates logs for every crash -- **Timestamped Files** - Unique log file for each crash -- **Recovery Status** - Tracks whether recovery was successful -- **Pattern Analysis** - Analyzes logs for trends - -## Performance Impact - -### Memory Usage -- **Native Crash Prevention**: ~2-3MB additional memory -- **Crash Monitor**: ~3-5MB additional memory -- **Total Additional Memory**: ~5-8MB - -### CPU Impact -- **Health Checks**: Minimal background processing -- **Pattern Analysis**: Low-priority background tasks -- **Recovery Operations**: Only when crashes are detected - -### Startup Time -- **Initialization**: ~100-150ms additional startup time -- **Health Monitoring**: ~50ms setup time -- **Total Additional Time**: ~150-200ms - -## Configuration Options - -### Recovery Strategy Priority -```java -// Customize recovery strategy priorities -sRecoveryStrategies.put("JavaException", new JavaExceptionRecovery()); // Priority: 100 -sRecoveryStrategies.put("NativeCrash", new NativeCrashRecovery()); // Priority: 90 -sRecoveryStrategies.put("DexCorruption", new DexCorruptionRecovery()); // Priority: 80 -sRecoveryStrategies.put("WebViewCrash", new WebViewCrashRecovery()); // Priority: 70 -sRecoveryStrategies.put("MemoryCrash", new MemoryCrashRecovery()); // Priority: 60 -``` - -### Health Check Intervals -```java -// Health check interval (30 seconds) -sMainHandler.postDelayed(this, 30000); - -// Pattern analysis interval (60 seconds) -sMainHandler.postDelayed(this, 60000); -``` - -### Memory Thresholds -```java -// Memory usage threshold for cleanup -if (memoryUsagePercent > 80) { - System.gc(); // Force garbage collection -} - -// Thread count threshold -if (threadCount > 100) { - Slog.w(TAG, "High thread count detected: " + threadCount); -} -``` - -## Monitoring and Debugging - -### Status Commands -```java -// Get comprehensive status for all systems -String nativeStatus = NativeCrashPrevention.getStatus(); -String monitorStatus = CrashMonitor.getStatus(); -String crashStats = CrashMonitor.getCrashStats(); - -Slog.d(TAG, "Native Prevention: " + nativeStatus); -Slog.d(TAG, "Crash Monitor: " + monitorStatus); -Slog.d(TAG, "Crash Stats: " + crashStats); -``` - -### Debug Commands -```bash -# Check logs for all crash prevention systems -adb logcat | grep -E "(NativeCrashPrevention|CrashMonitor|CrashRecovery)" - -# Check crash log files -adb shell ls -la /data/data/*/files/crash_logs/ - -# Check system health -adb shell dumpsys meminfo -adb shell dumpsys activity processes -``` - -## Expected Results - -### Before Additional Fixes -- Apps still crashing despite DEX fixes -- Native crashes not handled -- No crash pattern analysis -- Limited recovery mechanisms - -### After Additional Fixes -✅ **Native Crashes Handled** - SIGSEGV, SIGABRT, and other native crashes prevented -✅ **Memory Issues Resolved** - Automatic memory cleanup and monitoring -✅ **Thread Problems Fixed** - Thread health monitoring and recovery -✅ **Pattern Detection** - Automatic identification of problematic patterns -✅ **Proactive Prevention** - Health monitoring prevents issues before they occur -✅ **Comprehensive Recovery** - Multiple recovery strategies for any crash type - -## Troubleshooting - -### Common Issues - -1. **Native Crashes Still Occurring** - - Check if NativeCrashPrevention is initialized - - Verify signal handlers are installed - - Check logcat for native crash prevention logs - -2. **Memory Issues Persist** - - Check memory monitoring logs - - Verify garbage collection is working - - Check for memory leaks in apps - -3. **Thread Problems Continue** - - Check thread monitoring logs - - Verify thread count is reasonable - - Check for thread deadlocks - -### Debug Steps - -1. **Check System Status** - ```java - String status = CrashMonitor.getStatus(); - String nativeStatus = NativeCrashPrevention.getStatus(); - ``` - -2. **Force Health Check** - ```java - // Health checks are automatic, but you can trigger them - System.gc(); // Force garbage collection - ``` - -3. **Clear All Caches** - ```java - NativeCrashPrevention.clearCache(); - CrashMonitor.clearCrashHistory(); - ``` - -## Future Enhancements - -### Planned Improvements - -1. **Machine Learning Integration** - - Predict crashes before they occur - - Pattern recognition for crash types - - Automated recovery strategy optimization - -2. **Advanced Native Handling** - - JNI-based signal handlers - - Native library corruption detection - - Advanced memory protection - -3. **Performance Optimization** - - Lazy loading of recovery strategies - - Intelligent health check scheduling - - Background optimization - -4. **Advanced Monitoring** - - Real-time crash prediction - - Performance impact analysis - - Automated testing and validation - -## Conclusion - -These additional crash prevention systems provide: - -- **Comprehensive Coverage** - Handles all types of crashes (Java, Native, DEX, WebView, Memory) -- **Real-time Monitoring** - Continuous health monitoring and crash detection -- **Automatic Recovery** - Multiple recovery strategies for any failure scenario -- **Pattern Analysis** - Identifies problematic patterns before they cause widespread issues -- **Proactive Prevention** - Prevents crashes before they occur through health monitoring - -The implementation creates a robust, multi-layered crash prevention system that should eliminate the remaining crashes while providing comprehensive monitoring and recovery capabilities. - -### Key Benefits - -✅ **All Crash Types Handled** - Java, Native, DEX, WebView, Memory, Thread crashes -✅ **Real-time Recovery** - Automatic crash recovery with multiple strategies -✅ **Health Monitoring** - Continuous system health monitoring -✅ **Pattern Detection** - Automatic identification of problematic patterns -✅ **Proactive Prevention** - Prevents issues before they cause crashes -✅ **Comprehensive Logging** - Detailed crash logs for analysis and debugging - -The system is now equipped to handle virtually any type of crash scenario while maintaining system stability and performance. diff --git a/APK_PATH_FIX.md b/APK_PATH_FIX.md deleted file mode 100644 index 9dfd791..0000000 --- a/APK_PATH_FIX.md +++ /dev/null @@ -1,258 +0,0 @@ -# APK Path Fix for Missing APK Files - -## Problem Analysis - -The app was crashing with I/O errors when trying to load APK files that don't exist: - -``` -W Unable to open '/system/app/com.twitter.android.apk': No such file or directory -E Failed to open APK '/system/app/com.twitter.android.apk': I/O error -E failed to preload asset path '/system/app/com.twitter.android.apk' -E Error creating package context for com.twitter.android: Application package com.twitter.android not found -``` - -**Root Cause:** The fallback `ApplicationInfo` was using fake paths like `/system/app/com.twitter.android.apk` that don't exist on the device, causing the system to fail when trying to load resources from these non-existent APK files. - -## Issues Identified - -1. **Fake APK Paths**: Fallback methods were using hardcoded paths that don't exist -2. **No Path Validation**: No checking if APK files actually exist before using them -3. **Resource Loading Failures**: System trying to load resources from non-existent APK files -4. **Package Context Creation Failures**: Unable to create package context due to missing APK -5. **No Fallback for Missing APKs**: No mechanism to handle cases where APK files are not available - -## Fixes Implemented - -### 1. Enhanced Fallback ApplicationInfo Creation (`BPackageManager.java`) - -**Improved the `createFallbackApplicationInfo` method:** - -```java -private ApplicationInfo createFallbackApplicationInfo(String packageName, int flags, int userId) { - Log.w(TAG, "Creating fallback ApplicationInfo for " + packageName); - ApplicationInfo info = new ApplicationInfo(); - info.packageName = packageName; - info.flags = flags; - info.uid = 0; // Placeholder - - // Use more realistic paths that are less likely to cause issues - String apkPath = findActualApkPath(packageName); - if (apkPath != null) { - info.sourceDir = apkPath; - info.publicSourceDir = apkPath; - } else { - // Use data app path instead of system app path - info.sourceDir = "/data/app/" + packageName + "-1/base.apk"; - info.publicSourceDir = "/data/app/" + packageName + "-1/base.apk"; - } - - info.dataDir = "/data/data/" + packageName; - info.nativeLibraryDir = "/data/app-lib/" + packageName; - info.metaData = new Bundle(); - info.splitNames = new String[]{}; - - // Set some basic flags to make it look more realistic - info.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP; - info.flags |= ApplicationInfo.FLAG_SUPPORTS_RTL; - - return info; -} -``` - -### 2. Real APK Path Discovery (`BPackageManager.java`) - -**Added `findActualApkPath` method to find real APK locations:** - -```java -private String findActualApkPath(String packageName) { - try { - // Try to get the real application info from the system PackageManager - ApplicationInfo realInfo = BlackBoxCore.getContext().getPackageManager() - .getApplicationInfo(packageName, 0); - if (realInfo != null && realInfo.sourceDir != null) { - Log.d(TAG, "Found real APK path for " + packageName + ": " + realInfo.sourceDir); - return realInfo.sourceDir; - } - } catch (Exception e) { - Log.d(TAG, "Could not find real APK path for " + packageName + ": " + e.getMessage()); - } - - // Try common paths - String[] commonPaths = { - "/data/app/" + packageName + "-1/base.apk", - "/data/app/" + packageName + "-2/base.apk", - "/data/app/" + packageName + "/base.apk", - "/system/app/" + packageName + ".apk", - "/system/priv-app/" + packageName + ".apk" - }; - - for (String path : commonPaths) { - if (new File(path).exists()) { - Log.d(TAG, "Found existing APK at: " + path); - return path; - } - } - - Log.w(TAG, "No existing APK found for " + packageName + ", using fallback path"); - return null; -} -``` - -### 3. Enhanced Package Context Creation (`BActivityThread.java`) - -**Improved `createPackageContext` method with better error handling:** - -```java -public static Context createPackageContext(ApplicationInfo info) { - try { - // First, try to create the package context normally - return BlackBoxCore.getContext().createPackageContext(info.packageName, - Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); - } catch (SecurityException se) { - Slog.e(TAG, "SecurityException creating package context for " + info.packageName + ": " + se.getMessage()); - // Try alternative approach for sandboxed environments - try { - return BlackBoxCore.getContext().createPackageContext(info.packageName, - Context.CONTEXT_INCLUDE_CODE); - } catch (Exception e2) { - Slog.e(TAG, "Alternative package context creation also failed: " + e2.getMessage()); - } - } catch (Exception e) { - Slog.e(TAG, "Error creating package context for " + info.packageName + ": " + e.getMessage()); - - // If the error is related to missing APK, try to create a minimal context - if (e.getMessage() != null && e.getMessage().contains("not found")) { - Slog.w(TAG, "Package not found, attempting to create minimal context for " + info.packageName); - return createMinimalPackageContext(info); - } - } - return null; -} -``` - -### 4. Minimal Package Context Creation (`BActivityThread.java`) - -**Added `createMinimalPackageContext` method for when APK is not available:** - -```java -private static Context createMinimalPackageContext(ApplicationInfo info) { - try { - // Create a context that doesn't require the actual APK - Context baseContext = BlackBoxCore.getContext(); - - // Try to create a context with minimal flags - return baseContext.createPackageContext(info.packageName, 0); - } catch (Exception e) { - Slog.e(TAG, "Failed to create minimal package context for " + info.packageName + ": " + e.getMessage()); - - // Last resort: return the base context - Slog.w(TAG, "Using base context as fallback for " + info.packageName); - return BlackBoxCore.getContext(); - } -} -``` - -### 5. Enhanced Application Binding (`BActivityThread.java`) - -**Improved `handleBindApplication` to handle package context failures:** - -```java -Context packageContext = createPackageContext(applicationInfo); -if (packageContext == null) { - Slog.e(TAG, "Failed to create package context for " + packageName); - - // Try to create a minimal application without package context - Slog.w(TAG, "Attempting to create minimal application for " + packageName); - try { - Application minimalApp = createMinimalApplication(packageName, processName); - if (minimalApp != null) { - Slog.d(TAG, "Successfully created minimal application for " + packageName); - mInitialApplication = minimalApp; - BRActivityThread.get(BlackBoxCore.mainThread())._set_mInitialApplication(mInitialApplication); - - // Skip the rest of the binding process for minimal app - onBeforeApplicationOnCreate(packageName, processName, minimalApp); - AppInstrumentation.get().callApplicationOnCreate(minimalApp); - onAfterApplicationOnCreate(packageName, processName, minimalApp); - return; - } - } catch (Exception e) { - Slog.e(TAG, "Failed to create minimal application for " + packageName, e); - } - - throw new RuntimeException("Unable to create package context"); -} -``` - -### 6. Minimal Application Creation (`BActivityThread.java`) - -**Added methods to create minimal applications when normal creation fails:** - -```java -private Application createMinimalApplication(String packageName, String processName) { - try { - Slog.d(TAG, "Creating minimal application for " + packageName); - - // Create a basic Application object - Application app = new Application() { - @Override - public void onCreate() { - super.onCreate(); - Slog.d(TAG, "Minimal application onCreate called for " + packageName); - } - }; - - // Set up basic context - try { - Method attachBaseContext = Application.class.getDeclaredMethod("attachBaseContext", Context.class); - attachBaseContext.setAccessible(true); - attachBaseContext.invoke(app, BlackBoxCore.getContext()); - } catch (Exception e) { - Slog.w(TAG, "Could not attach base context to minimal application: " + e.getMessage()); - } - - return app; - } catch (Exception e) { - Slog.e(TAG, "Error creating minimal application for " + packageName, e); - return null; - } -} -``` - -## Expected Results - -After implementing these fixes, the app should: - -✅ **Handle missing APK files gracefully** without crashing -✅ **Find real APK paths** when available -✅ **Use realistic fallback paths** when real paths aren't found -✅ **Create minimal contexts** when package contexts fail -✅ **Create minimal applications** when normal creation fails -✅ **Continue functioning** even with limited APK availability -✅ **Provide detailed logging** for debugging APK issues - -## Testing Recommendations - -1. **Test with missing APKs** to verify fallback mechanisms -2. **Test with different APK locations** (system, data, priv-app) -3. **Monitor logcat** for APK path discovery messages -4. **Test app startup** on various devices -5. **Verify minimal application creation** works correctly -6. **Test with sandboxed environments** that have limited access - -## Additional Notes - -- The fixes maintain backward compatibility -- Real APK paths are preferred over fallback paths -- Multiple fallback mechanisms ensure app stability -- Enhanced logging helps with debugging APK issues -- Minimal applications provide basic functionality when full apps can't be created - -## Build Instructions - -1. Clean and rebuild the project in Android Studio -2. Test app startup on the target device -3. Monitor logcat for APK-related messages -4. Verify that missing APK errors are handled gracefully - -The comprehensive APK path handling and fallback mechanisms implemented should resolve the I/O errors and allow the app to start successfully even when APK files are not available or accessible. diff --git a/APK_PATH_VALIDATION_FIX.md b/APK_PATH_VALIDATION_FIX.md deleted file mode 100644 index 56a2e6d..0000000 --- a/APK_PATH_VALIDATION_FIX.md +++ /dev/null @@ -1,267 +0,0 @@ -# APK Path Validation Fix for Missing APK Files - -## Problem Analysis - -The app was still crashing with I/O errors when trying to load APK files that don't exist: - -``` -W Unable to open '/data/app/com.twitter.android-1/base.apk': No such file or directory -E Failed to open APK '/data/app/com.twitter.android-1/base.apk': I/O error -E failed to add asset path '/data/app/com.twitter.android-1/base.apk' -``` - -**Root Cause:** The fallback mechanism was still providing fake APK paths that don't exist on the device, causing the system to fail when trying to load resources from these non-existent APK files. - -## Issues Identified - -1. **Fake Fallback Paths**: Still using hardcoded paths that don't exist -2. **No Path Validation**: No checking if APK files are actually valid and accessible -3. **I/O Errors**: System trying to load resources from non-existent APK files -4. **No Null Handling**: Not properly handling cases where no APK exists -5. **Insufficient Context Creation**: Package context creation not handling null sourceDir - -## Fixes Implemented - -### 1. Null Path Fallback (`BPackageManager.java`) - -**Improved the `createFallbackApplicationInfo` method to use null paths:** - -```java -private ApplicationInfo createFallbackApplicationInfo(String packageName, int flags, int userId) { - Log.w(TAG, "Creating fallback ApplicationInfo for " + packageName); - ApplicationInfo info = new ApplicationInfo(); - info.packageName = packageName; - info.flags = flags; - info.uid = 0; // Placeholder - - // Use more realistic paths that are less likely to cause issues - String apkPath = findActualApkPath(packageName); - if (apkPath != null) { - info.sourceDir = apkPath; - info.publicSourceDir = apkPath; - } else { - // If no APK exists, use null or existing system paths to prevent I/O errors - Log.w(TAG, "No APK found for " + packageName + ", using null paths to prevent I/O errors"); - info.sourceDir = null; // Use null instead of fake path - info.publicSourceDir = null; // Use null instead of fake path - } - - info.dataDir = "/data/data/" + packageName; - info.nativeLibraryDir = "/data/app-lib/" + packageName; - info.metaData = new Bundle(); - info.splitNames = new String[]{}; - - // Set some basic flags to make it look more realistic - info.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP; - info.flags |= ApplicationInfo.FLAG_SUPPORTS_RTL; - - return info; -} -``` - -### 2. Enhanced Package Context Creation (`BActivityThread.java`) - -**Improved `createPackageContext` to handle null sourceDir:** - -```java -public static Context createPackageContext(ApplicationInfo info) { - try { - // Check if the ApplicationInfo has a valid sourceDir - if (info.sourceDir == null) { - Slog.w(TAG, "ApplicationInfo has null sourceDir for " + info.packageName + ", using minimal context"); - return createMinimalPackageContext(info); - } - - // First, try to create the package context normally - return BlackBoxCore.getContext().createPackageContext(info.packageName, - Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); - } catch (SecurityException se) { - Slog.e(TAG, "SecurityException creating package context for " + info.packageName + ": " + se.getMessage()); - // Try alternative approach for sandboxed environments - try { - return BlackBoxCore.getContext().createPackageContext(info.packageName, - Context.CONTEXT_INCLUDE_CODE); - } catch (Exception e2) { - Slog.e(TAG, "Alternative package context creation also failed: " + e2.getMessage()); - } - } catch (Exception e) { - Slog.e(TAG, "Error creating package context for " + info.packageName + ": " + e.getMessage()); - - // If the error is related to missing APK, try to create a minimal context - if (e.getMessage() != null && e.getMessage().contains("not found")) { - Slog.w(TAG, "Package not found, attempting to create minimal context for " + info.packageName); - return createMinimalPackageContext(info); - } - } - return null; -} -``` - -### 3. Enhanced Minimal Package Context (`BActivityThread.java`) - -**Improved `createMinimalPackageContext` with multiple fallback strategies:** - -```java -private static Context createMinimalPackageContext(ApplicationInfo info) { - try { - // Create a context that doesn't require the actual APK - Context baseContext = BlackBoxCore.getContext(); - - // Try to create a context with minimal flags - try { - return baseContext.createPackageContext(info.packageName, 0); - } catch (Exception e) { - Slog.w(TAG, "Failed to create package context with minimal flags for " + info.packageName + ": " + e.getMessage()); - } - - // Try to create a context without any flags - try { - return baseContext.createPackageContext(info.packageName, Context.CONTEXT_IGNORE_SECURITY); - } catch (Exception e) { - Slog.w(TAG, "Failed to create package context with ignore security for " + info.packageName + ": " + e.getMessage()); - } - - } catch (Exception e) { - Slog.e(TAG, "Failed to create minimal package context for " + info.packageName + ": " + e.getMessage()); - } - - // Last resort: return the base context - Slog.w(TAG, "Using base context as fallback for " + info.packageName); - return BlackBoxCore.getContext(); -} -``` - -### 4. APK Path Validation (`BPackageManager.java`) - -**Added `isValidApkPath` method for robust path validation:** - -```java -private boolean isValidApkPath(String path) { - try { - File apkFile = new File(path); - if (!apkFile.exists()) { - return false; - } - - // Additional validation: check if it's readable and has reasonable size - if (!apkFile.canRead()) { - Log.d(TAG, "APK file not readable: " + path); - return false; - } - - long fileSize = apkFile.length(); - if (fileSize < 1024) { // Less than 1KB is probably not a valid APK - Log.d(TAG, "APK file too small: " + path + " (size: " + fileSize + ")"); - return false; - } - - return true; - } catch (Exception e) { - Log.d(TAG, "Error checking APK path " + path + ": " + e.getMessage()); - return false; - } -} -``` - -### 5. Enhanced Path Discovery (`BPackageManager.java`) - -**Improved `findActualApkPath` to use validation:** - -```java -private String findActualApkPath(String packageName) { - if (sIsFindingApkPath) { - Log.w(TAG, "findActualApkPath called recursively, returning null to prevent infinite loop."); - return null; - } - sIsFindingApkPath = true; - try { - // Skip PackageManager call to prevent infinite recursion - Log.d(TAG, "Skipping PackageManager call to prevent recursion for " + packageName); - - // Try common paths - String[] commonPaths = { - "/data/app/" + packageName + "-1/base.apk", - "/data/app/" + packageName + "-2/base.apk", - "/data/app/" + packageName + "/base.apk", - "/system/app/" + packageName + ".apk", - "/system/priv-app/" + packageName + ".apk" - }; - - for (String path : commonPaths) { - if (isValidApkPath(path)) { - Log.d(TAG, "Found existing APK at: " + path); - return path; - } - } - - Log.w(TAG, "No existing APK found for " + packageName + ", using null path"); - return null; - } finally { - sIsFindingApkPath = false; // Reset flag - } -} -``` - -## Key Changes Made - -### 1. **Null Path Handling** -- Use `null` for `sourceDir` and `publicSourceDir` when no APK exists -- Prevents I/O errors from non-existent APK files -- Allows graceful degradation - -### 2. **Enhanced Path Validation** -- Check if APK file exists and is readable -- Validate file size (minimum 1KB) -- Comprehensive error handling - -### 3. **Multiple Context Creation Strategies** -- Try different context creation flags -- Fallback to base context as last resort -- Better error handling and logging - -### 4. **Improved Error Handling** -- Check for null `sourceDir` before attempting context creation -- Multiple fallback mechanisms -- Detailed logging for debugging - -### 5. **Robust Path Discovery** -- Validate APK paths before using them -- Skip invalid or inaccessible files -- Return null instead of fake paths - -## Expected Results - -After implementing these fixes, the app should: - -✅ **No more I/O errors** from non-existent APK files -✅ **Graceful handling** of missing APK files -✅ **Proper null path handling** in ApplicationInfo -✅ **Multiple context creation strategies** for different scenarios -✅ **Robust path validation** before using APK files -✅ **Detailed logging** for debugging path issues - -## Testing Recommendations - -1. **Test with missing APKs** to verify null path handling -2. **Test with different APK locations** (data, system, priv-app) -3. **Monitor logcat** for path validation messages -4. **Test app startup** on various devices -5. **Verify context creation** works with null sourceDir -6. **Test with corrupted APK files** to verify validation - -## Additional Notes - -- The fixes maintain backward compatibility -- Null paths are safer than fake paths that don't exist -- Enhanced validation prevents using invalid APK files -- Multiple fallback strategies ensure app stability -- Detailed logging helps with debugging and monitoring - -## Build Instructions - -1. Clean and rebuild the project in Android Studio -2. Test app startup on the target device -3. Monitor logcat for path validation messages -4. Verify that I/O errors are resolved - -The comprehensive APK path validation and null path handling implemented should resolve the I/O errors and allow the app to start successfully even when APK files are not available or accessible. diff --git a/Bcore/build.gradle b/Bcore/build.gradle index 41b817a..6941e03 100644 --- a/Bcore/build.gradle +++ b/Bcore/build.gradle @@ -80,27 +80,16 @@ tasks.withType(Javadoc) { dependencies { - implementation 'com.google.android.material:material:1.3.0' - implementation "com.github.CodingGay.BlackReflection:core:$rootProject.ext.blackReflection" - annotationProcessor "com.github.CodingGay.BlackReflection:compiler:$rootProject.ext.blackReflection" - - /* // Configure annotation processors to be incremental - annotationProcessor { - arguments { - // Enable incremental processing - arg("incremental", "true") - // Set source version compatibility - arg("sourceVersion", "17") - } - }*/ - implementation fileTree(dir: "libs", include: ["*.jar", "*.aar"]) + implementation 'com.google.android.material:material:1.3.0' + implementation(project(":black-reflection")) + annotationProcessor(project(":compiler")) implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'com.moandjiezana.toml:toml4j:0.7.2' implementation 'com.github.tiann:FreeReflection:3.2.2' - implementation "com.github.CodingGay.BlackReflection:core:$rootProject.ext.blackReflection" +// implementation "com.github.CodingGay.BlackReflection:core:$rootProject.ext.blackReflection" // Pine dependency removed diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/BlackBoxCore.java b/Bcore/src/main/java/top/niunaijun/blackbox/BlackBoxCore.java index ff500ae..09aba04 100644 --- a/Bcore/src/main/java/top/niunaijun/blackbox/BlackBoxCore.java +++ b/Bcore/src/main/java/top/niunaijun/blackbox/BlackBoxCore.java @@ -69,9 +69,7 @@ import top.niunaijun.blackbox.utils.SocialMediaAppCrashPrevention; import top.niunaijun.blackbox.utils.DexCrashPrevention; import top.niunaijun.blackbox.utils.NativeCrashPrevention; import top.niunaijun.blackbox.utils.CrashMonitor; - - - +import top.niunaijun.blackbox.utils.StoragePermissionHelper; // just use it guys and i know you forgot to give credits so have it what ever 🤧 /** * updated by alex5402 on 4/9/21. @@ -1077,9 +1075,112 @@ public class BlackBoxCore extends ClientConfiguration { public static BStorageManager getBStorageManager() { return BStorageManager.get(); } + + // ============== Storage Permission Methods (SDK 30+) ============== + + /** + * Check if the host app has storage permission. + * On Android 13+, this checks for granular media permissions. + * On Android 11-12, this checks for READ_EXTERNAL_STORAGE or MANAGE_EXTERNAL_STORAGE. + */ + public boolean hasStoragePermission() { + return StoragePermissionHelper.hasStoragePermission(sContext); + } + + /** + * Check if the host app has "All Files Access" (MANAGE_EXTERNAL_STORAGE) on Android 11+. + * Returns true on older Android versions where this isn't required. + */ + public boolean hasAllFilesAccess() { + return StoragePermissionHelper.hasAllFilesAccess(); + } + + /** + * Check if the host app has full file access (both storage permission and all files access). + */ + public boolean hasFullFileAccess() { + return StoragePermissionHelper.hasFullFileAccess(sContext); + } + + /** + * Request storage permission from the user. + * On Android 13+, requests granular media permissions. + * On older versions, requests READ/WRITE_EXTERNAL_STORAGE. + * + * @param activity The activity to use for requesting permission + */ + public void requestStoragePermission(android.app.Activity activity) { + StoragePermissionHelper.requestStoragePermission(activity); + } + + /** + * Request "All Files Access" (MANAGE_EXTERNAL_STORAGE) on Android 11+. + * This opens the system settings page for the user to grant permission. + * + * @param activity The activity to use for requesting permission + */ + public void requestAllFilesAccess(android.app.Activity activity) { + StoragePermissionHelper.requestAllFilesAccess(activity); + } + + /** + * Request full file access (both storage permission and all files access). + * Use this before launching apps that need file access. + * + * @param activity The activity to use for requesting permission + */ + public void requestFullFileAccess(android.app.Activity activity) { + StoragePermissionHelper.requestFullFileAccess(activity); + } + + /** + * Handle permission result callback from Activity.onRequestPermissionsResult() + * @return true if storage permission was granted + */ + public boolean handleStoragePermissionResult(android.app.Activity activity, int requestCode, + String[] permissions, int[] grantResults) { + return StoragePermissionHelper.handlePermissionResult(activity, requestCode, permissions, grantResults); + } + + /** + * Handle result from All Files Access settings activity. + * Call this from Activity.onActivityResult() + * @return true if all files access was granted + */ + public boolean handleAllFilesAccessResult(int requestCode) { + return StoragePermissionHelper.handleAllFilesAccessResult(requestCode); + } + + /** + * Get the request codes for permission handling. + */ + public static int getStoragePermissionRequestCode() { + return StoragePermissionHelper.REQUEST_CODE_STORAGE_PERMISSION; + } + + public static int getAllFilesAccessRequestCode() { + return StoragePermissionHelper.REQUEST_CODE_MANAGE_STORAGE; + } public boolean launchApk(String packageName, int userId) { - onBeforeMainLaunchApk(packageName,userId); + onBeforeMainLaunchApk(packageName, userId); + + // Check storage permissions on Android 11+ (SDK 30+) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + if (!hasAllFilesAccess()) { + Slog.w(TAG, "All files access not granted for launching: " + packageName); + // Notify the host app that permission is needed + for (AppLifecycleCallback callback : mAppLifecycleCallbacks) { + if (callback.onStoragePermissionNeeded(packageName, userId)) { + // Host app will handle the permission request, cancel launch + Slog.d(TAG, "Launch cancelled - host app handling permission request"); + return false; + } + } + // Otherwise, continue but warn + Slog.w(TAG, "Launching without all files access - some file operations may fail"); + } + } Intent launchIntentForPackage = getBPackageManager().getLaunchIntentForPackage(packageName, userId); if (launchIntentForPackage == null) { diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/app/configuration/AppLifecycleCallback.java b/Bcore/src/main/java/top/niunaijun/blackbox/app/configuration/AppLifecycleCallback.java index a0bcc3e..56409ed 100644 --- a/Bcore/src/main/java/top/niunaijun/blackbox/app/configuration/AppLifecycleCallback.java +++ b/Bcore/src/main/java/top/niunaijun/blackbox/app/configuration/AppLifecycleCallback.java @@ -22,6 +22,20 @@ public class AppLifecycleCallback implements Application.ActivityLifecycleCallba public void beforeMainLaunchApk(String packageName, int userid) { } + + /** + * Called when storage permission is needed before launching an app. + * Override this in your host app to show permission request UI. + * + * @param packageName The package being launched + * @param userId The user ID + * @return true if the host app will handle the permission request (launch will be cancelled), + * false to continue launching anyway + */ + public boolean onStoragePermissionNeeded(String packageName, int userId) { + // Default: don't block the launch, just warn + return false; + } public void beforeMainApplicationAttach(Application app, Context context) { diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/HookManager.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/HookManager.java index a065799..a2043a6 100644 --- a/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/HookManager.java +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/HookManager.java @@ -19,6 +19,7 @@ import top.niunaijun.blackbox.fake.service.IAppOpsManagerProxy; import top.niunaijun.blackbox.fake.service.IAppWidgetManagerProxy; import top.niunaijun.blackbox.fake.service.IAttributionSourceProxy; import top.niunaijun.blackbox.fake.service.IAutofillManagerProxy; +import top.niunaijun.blackbox.fake.service.ISensitiveContentProtectionManagerProxy; import top.niunaijun.blackbox.fake.service.ISettingsSystemProxy; import top.niunaijun.blackbox.fake.service.IConnectivityManagerProxy; import top.niunaijun.blackbox.fake.service.ISystemSensorManagerProxy; @@ -192,6 +193,10 @@ public class HookManager { addInjector(new IActivityClientProxy(null)); addInjector(new IVpnManagerProxy()); } + // 14.0 (Safe to try on S+, will skip if service missing) + if (BuildCompat.isS()) { + addInjector(new ISensitiveContentProtectionManagerProxy()); + } // 11.0 if (BuildCompat.isR()) { addInjector(new IPermissionManagerProxy()); diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/BrowserEngineProxy.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/BrowserEngineProxy.java new file mode 100644 index 0000000..6815d10 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/BrowserEngineProxy.java @@ -0,0 +1,94 @@ +package top.niunaijun.blackbox.fake.service; + +import android.os.Build; +import android.os.Process; +import java.lang.reflect.Method; + +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.fake.hook.ClassInvocationStub; +import top.niunaijun.blackbox.fake.hook.MethodHook; +import top.niunaijun.blackbox.fake.hook.ProxyMethod; +import top.niunaijun.blackbox.utils.Slog; + +/** + * Proxy to handle browser-specific (GeckoView/Chromium) issues in virtualized environment. + * Browsers use advanced sandboxing and multi-process architecture that can conflict with virtualization. + */ +public class BrowserEngineProxy extends ClassInvocationStub { + public static final String TAG = "BrowserEngineProxy"; + + public BrowserEngineProxy() { + super(); + } + + @Override + protected Object getWho() { + return null; + } + + @Override + protected void inject(Object baseInvocation, Object proxyInvocation) { + // Not needed for class method hooks + } + + @Override + public boolean isBadEnv() { + return false; + } + + /** + * Hook Process.start() to handle browser child process creation + * Browsers (especially Gecko/Chromium) spawn isolated child processes for rendering + */ + @ProxyMethod("start") + public static class ProcessStart extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + try { + // Let the process start normally + Object result = method.invoke(who, args); + + // Log for debugging + if (args != null && args.length > 0) { + Slog.d(TAG, "Process.start() called with args count: " + args.length); + } + + return result; + } catch (Exception e) { + Slog.e(TAG, "Process.start() failed: " + e.getMessage(), e); + // Re-throw to let the app handle it + throw e; + } + } + } + + /** + * Hook Process.setArgV0() which browsers use to rename child processes + */ + @ProxyMethod("setArgV0") + public static class SetArgV0 extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + if (args != null && args.length > 0) { + String argV0 = (String) args[0]; + Slog.d(TAG, "Process.setArgV0() called: " + argV0); + } + return method.invoke(who, args); + } + } + + /** + * Hook Process.killProcess() to log unexpected terminations + */ + @ProxyMethod("killProcess") + public static class KillProcess extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + if (args != null && args.length > 0) { + int pid = (int) args[0]; + Slog.d(TAG, "Process.killProcess() called for PID: " + pid); + } + return method.invoke(who, args); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IAppOpsManagerProxy.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IAppOpsManagerProxy.java index aad6758..c2642f0 100644 --- a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IAppOpsManagerProxy.java +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IAppOpsManagerProxy.java @@ -50,17 +50,36 @@ public class IAppOpsManagerProxy extends BinderInvocationStub { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String methodName = method.getName(); + + // For all check, note, and start operations, return MODE_ALLOWED directly + // without calling the system to avoid UID mismatch errors + if (methodName.startsWith("check") || + methodName.startsWith("note") || + methodName.startsWith("start")) { + Slog.d(TAG, "AppOps invoke: Bypassing system for " + methodName + ", allowing operation"); + return AppOpsManager.MODE_ALLOWED; + } + + // For finish operations, just return null without calling system + if (methodName.startsWith("finish")) { + Slog.d(TAG, "AppOps invoke: Bypassing system for " + methodName); + return null; + } + + // For other operations, try to call system with error handling try { MethodParameterUtils.replaceFirstAppPkg(args); MethodParameterUtils.replaceLastUid(args); return super.invoke(proxy, method, args); } catch (SecurityException e) { // Handle SecurityException for UID/package mismatches - Slog.w(TAG, "AppOps invoke: SecurityException caught, allowing operation", e); + Slog.w(TAG, "AppOps invoke: SecurityException caught for " + methodName + ", allowing operation", e); return AppOpsManager.MODE_ALLOWED; } catch (Exception e) { - Slog.e(TAG, "AppOps invoke: Error in method " + method.getName(), e); - return super.invoke(proxy, method, args); + Slog.e(TAG, "AppOps invoke: Error in method " + methodName, e); + // Return MODE_ALLOWED as fallback to prevent crashes + return AppOpsManager.MODE_ALLOWED; } } @@ -90,19 +109,21 @@ public class IAppOpsManagerProxy extends BinderInvocationStub { public static class CheckOperation extends MethodHook { @Override protected Object hook(Object who, Method method, Object[] args) throws Throwable { - MethodParameterUtils.replaceLastUid(args); - try { - // args: (int op, int uid, String packageName) - int op = (int) args[0]; - // On API >= 29, translate to public op name and allow media/storage reads - String publicName = getOpPublicName(op); - if (publicName != null && isMediaStorageOrAudioOp(publicName)) { - Slog.d(TAG, "AppOps CheckOperation: Allowing operation: " + publicName); - return AppOpsManager.MODE_ALLOWED; - } - } catch (Throwable ignored) { - } - return method.invoke(who, args); + // Always return MODE_ALLOWED to bypass UID mismatch errors + // The system's checkOperation causes SecurityException when package/uid don't match + Slog.d(TAG, "AppOps CheckOperation: Bypassing system check, allowing operation"); + return AppOpsManager.MODE_ALLOWED; + } + } + + // Hook for checkOperationForDevice - this is called on Android 12+ and shown in stack trace + @ProxyMethod("checkOperationForDevice") + public static class CheckOperationForDevice extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + // Always return MODE_ALLOWED to bypass UID mismatch errors + Slog.d(TAG, "AppOps CheckOperationForDevice: Bypassing system check, allowing operation"); + return AppOpsManager.MODE_ALLOWED; } } @@ -110,17 +131,9 @@ public class IAppOpsManagerProxy extends BinderInvocationStub { public static class NoteOperation extends MethodHook { @Override protected Object hook(Object who, Method method, Object[] args) throws Throwable { - // args: (int op, int uid, String packageName) - try { - int op = (int) args[0]; - String publicName = getOpPublicName(op); - if (publicName != null && isMediaStorageOrAudioOp(publicName)) { - Slog.d(TAG, "AppOps NoteOperation: Allowing operation: " + publicName); - return AppOpsManager.MODE_ALLOWED; - } - } catch (Throwable ignored) { - } - return method.invoke(who, args); + // Always return MODE_ALLOWED to bypass UID mismatch errors + Slog.d(TAG, "AppOps NoteOperation: Bypassing system check, allowing operation"); + return AppOpsManager.MODE_ALLOWED; } } @@ -128,16 +141,9 @@ public class IAppOpsManagerProxy extends BinderInvocationStub { public static class CheckOpNoThrow extends MethodHook { @Override protected Object hook(Object who, Method method, Object[] args) throws Throwable { - // args: (String op, int uid, String packageName) - try { - String opStr = (String) args[0]; - if (opStr != null && isMediaStorageOrAudioOp(opStr)) { - Slog.d(TAG, "AppOps CheckOpNoThrow: Allowing operation: " + opStr); - return AppOpsManager.MODE_ALLOWED; - } - } catch (Throwable ignored) { - } - return method.invoke(who, args); + // Always return MODE_ALLOWED to bypass UID mismatch errors + Slog.d(TAG, "AppOps CheckOpNoThrow: Bypassing system check, allowing operation"); + return AppOpsManager.MODE_ALLOWED; } } @@ -146,16 +152,9 @@ public class IAppOpsManagerProxy extends BinderInvocationStub { public static class StartOp extends MethodHook { @Override protected Object hook(Object who, Method method, Object[] args) throws Throwable { - try { - int op = (int) args[0]; - String name = getOpPublicName(op); - if (name != null && isMediaStorageOrAudioOp(name)) { - Slog.d(TAG, "AppOps StartOp: Allowing operation: " + name); - return AppOpsManager.MODE_ALLOWED; - } - } catch (Throwable ignored) { - } - return method.invoke(who, args); + // Always return MODE_ALLOWED to bypass UID mismatch errors + Slog.d(TAG, "AppOps StartOp: Bypassing system check, allowing operation"); + return AppOpsManager.MODE_ALLOWED; } } @@ -163,16 +162,9 @@ public class IAppOpsManagerProxy extends BinderInvocationStub { public static class StartOpNoThrow extends MethodHook { @Override protected Object hook(Object who, Method method, Object[] args) throws Throwable { - try { - // args: (String op, int uid, String packageName) - String opStr = (String) args[0]; - if (opStr != null && isMediaStorageOrAudioOp(opStr)) { - Slog.d(TAG, "AppOps StartOpNoThrow: Allowing operation: " + opStr); - return AppOpsManager.MODE_ALLOWED; - } - } catch (Throwable ignored) { - } - return method.invoke(who, args); + // Always return MODE_ALLOWED to bypass UID mismatch errors + Slog.d(TAG, "AppOps StartOpNoThrow: Bypassing system check, allowing operation"); + return AppOpsManager.MODE_ALLOWED; } } diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IAttributionSourceProxy.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IAttributionSourceProxy.java index feda4ab..4c3d014 100644 --- a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IAttributionSourceProxy.java +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IAttributionSourceProxy.java @@ -47,7 +47,7 @@ public class IAttributionSourceProxy extends ClassInvocationStub { protected Object hook(Object who, Method method, Object[] args) throws Throwable { try { // Always create AttributionSource with correct UID to prevent crashes - int uid = BActivityThread.getBUid(); + int uid = BlackBoxCore.getHostUid(); String packageName = BlackBoxCore.getHostPkg(); Slog.d(TAG, "Creating AttributionSource with UID: " + uid + ", package: " + packageName); @@ -62,7 +62,7 @@ public class IAttributionSourceProxy extends ClassInvocationStub { return method.invoke(who, args); } catch (Exception e) { Slog.w(TAG, "Error creating AttributionSource, using fallback: " + e.getMessage()); - return createSafeAttributionSource(BActivityThread.getBUid(), BlackBoxCore.getHostPkg()); + return createSafeAttributionSource(BlackBoxCore.getHostUid(), BlackBoxCore.getHostPkg()); } } @@ -153,10 +153,10 @@ public class IAttributionSourceProxy extends ClassInvocationStub { } // If original method fails, create a safe fallback - return createSafeAttributionSource(BActivityThread.getBUid(), BlackBoxCore.getHostPkg()); + return createSafeAttributionSource(BlackBoxCore.getHostUid(), BlackBoxCore.getHostPkg()); } catch (Exception e) { Slog.w(TAG, "Error in fromParcel, using fallback: " + e.getMessage()); - return createSafeAttributionSource(BActivityThread.getBUid(), BlackBoxCore.getHostPkg()); + return createSafeAttributionSource(BlackBoxCore.getHostUid(), BlackBoxCore.getHostPkg()); } } @@ -166,9 +166,9 @@ public class IAttributionSourceProxy extends ClassInvocationStub { Class attributionSourceClass = attributionSource.getClass(); Method setUidMethod = attributionSourceClass.getDeclaredMethod("setUid", int.class); setUidMethod.setAccessible(true); - setUidMethod.invoke(attributionSource, BActivityThread.getBUid()); + setUidMethod.invoke(attributionSource, BlackBoxCore.getHostUid()); - Slog.d(TAG, "Fixed AttributionSource UID to: " + BActivityThread.getBUid()); + Slog.d(TAG, "Fixed AttributionSource UID to: " + BlackBoxCore.getHostUid()); } catch (Exception e) { Slog.w(TAG, "Could not fix AttributionSource UID: " + e.getMessage()); } diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/ISensitiveContentProtectionManagerProxy.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/ISensitiveContentProtectionManagerProxy.java new file mode 100644 index 0000000..b8622bc --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/ISensitiveContentProtectionManagerProxy.java @@ -0,0 +1,75 @@ +package top.niunaijun.blackbox.fake.service; + +import android.content.Context; +import android.os.IBinder; + +import java.lang.reflect.Method; + +import black.android.os.BRServiceManager; +import top.niunaijun.blackbox.BlackBoxCore; +import top.niunaijun.blackbox.fake.hook.BinderInvocationStub; +import top.niunaijun.blackbox.fake.hook.MethodHook; +import top.niunaijun.blackbox.fake.hook.ProxyMethod; +import top.niunaijun.blackbox.utils.Slog; + +/** + * Hook for ISensitiveContentProtectionManager (Android 14+) + * Fixes SecurityException: Specified calling package [pkg] does not match the calling uid + */ +public class ISensitiveContentProtectionManagerProxy extends BinderInvocationStub { + public static final String TAG = "ISensitiveContentProtection"; + + public ISensitiveContentProtectionManagerProxy() { + super(BRServiceManager.get().getService("sensitive_content_protection_service")); + } + + @Override + protected Object getWho() { + try { + IBinder binder = BRServiceManager.get().getService("sensitive_content_protection_service"); + if (binder == null) return null; + Class stubClass = Class.forName("android.view.ISensitiveContentProtectionManager$Stub"); + Method asInterface = stubClass.getMethod("asInterface", IBinder.class); + return asInterface.invoke(null, binder); + } catch (Exception e) { + Slog.d(TAG, "getWho error: " + e.getMessage()); + return null; + } + } + + @Override + protected void inject(Object baseInvocation, Object proxyInvocation) { + if (BRServiceManager.get().getService("sensitive_content_protection_service") != null) { + replaceSystemService("sensitive_content_protection_service"); + Slog.d(TAG, "Hooked SensitiveContentProtectionManagerService"); + } else { + Slog.d(TAG, "Skipping SensitiveContentProtectionManagerService hook (service not found)"); + } + } + + @Override + public boolean isBadEnv() { + IBinder binder = BRServiceManager.get().getService("sensitive_content_protection_service"); + return binder != null && binder != this; + } + + @ProxyMethod("setSensitiveContentProtection") + public static class SetSensitiveContentProtection extends MethodHook { + @Override + protected Object hook(Object who, Method method, Object[] args) throws Throwable { + if (args != null && args.length > 0) { + // setSensitiveContentProtection(IBinder windowToken, String packageName, boolean isProtected) + for (int i = 0; i < args.length; i++) { + if (args[i] instanceof String) { + String pkg = (String) args[i]; + if (pkg != null && !pkg.equals(BlackBoxCore.getHostPkg())) { + Slog.d(TAG, "Fixing package name in setSensitiveContentProtection: " + pkg + " -> " + BlackBoxCore.getHostPkg()); + args[i] = BlackBoxCore.getHostPkg(); + } + } + } + } + return method.invoke(who, args); + } + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/context/providers/ContentProviderStub.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/context/providers/ContentProviderStub.java index e4e99c3..6bd5a94 100644 --- a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/context/providers/ContentProviderStub.java +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/context/providers/ContentProviderStub.java @@ -6,10 +6,12 @@ import java.lang.reflect.Method; import black.android.content.BRAttributionSource; import top.niunaijun.blackbox.app.BActivityThread; +import top.niunaijun.blackbox.BlackBoxCore; import top.niunaijun.blackbox.fake.hook.ClassInvocationStub; import top.niunaijun.blackbox.utils.compat.ContextCompat; import top.niunaijun.blackbox.utils.Slog; import android.os.Bundle; +import top.niunaijun.blackbox.utils.AttributionSourceUtils; /** * updated by alex5402 on 4/8/21. @@ -52,39 +54,35 @@ public class ContentProviderStub extends ClassInvocationStub implements BContent return method.invoke(mBase, args); } - // Fix AttributionSource and package name issues - if (args != null && args.length > 0) { - for (int i = 0; i < args.length; i++) { - Object arg = args[i]; - if (arg instanceof String) { - String strArg = (String) arg; - // Don't replace system provider authorities - if (!isSystemProviderAuthority(strArg)) { - // Replace package name with the correct one - args[i] = mAppPkg; - } - } else if (arg != null && arg.getClass().getName().equals(BRAttributionSource.getRealClass().getName())) { - // Fix AttributionSource UID - try { - ContextCompat.fixAttributionSourceState(arg, BActivityThread.getBUid()); - } catch (Exception e) { - // If fixing AttributionSource fails, try to create a new one or skip - Slog.w(TAG, "Failed to fix AttributionSource, continuing with original"); - } - } else if (arg != null && arg.getClass().getName().contains("AttributionSource")) { - // Handle any AttributionSource-like objects that might cause UID issues - try { - // Try to fix UID using reflection - fixAttributionSourceUid(arg); - } catch (Exception e) { - Slog.w(TAG, "Failed to fix AttributionSource-like object: " + e.getMessage()); + // Get method name first to determine handling logic + String methodName = method.getName(); + + // For call() method, String args are method names like "GET_global", NOT package names + // Don't replace them! Only fix AttributionSource + if ("call".equals(methodName)) { + // Fix AttributionSource in args (treats Bundle recursively) + AttributionSourceUtils.fixAttributionSourceInArgs(args); + } else { + // For other methods like query/insert/update/delete, fix both package names and AttributionSource + if (args != null && args.length > 0) { + for (int i = 0; i < args.length; i++) { + Object arg = args[i]; + if (arg instanceof String) { + String strArg = (String) arg; + // Don't replace system provider authorities + if (!isSystemProviderAuthority(strArg)) { + // Replace package name with the correct one + args[i] = mAppPkg; + } } } + // Also fix AttributionSources in any arg position + AttributionSourceUtils.fixAttributionSourceInArgs(args); } } // Pre-validate the call to prevent system-level SecurityException - String methodName = method.getName(); + methodName = method.getName(); if (methodName.equals("query") || methodName.equals("insert") || methodName.equals("update") || methodName.equals("delete") || methodName.equals("bulkInsert") || methodName.equals("call")) { @@ -142,7 +140,7 @@ public class ContentProviderStub extends ClassInvocationStub implements BContent case "bulkInsert": return 0; // Return 0 rows inserted case "call": - return null; // Return null for call method + return new Bundle(); // Return empty Bundle instead of null to prevent NPE case "getType": return null; // Return null MIME type case "openFile": @@ -225,21 +223,21 @@ public class ContentProviderStub extends ClassInvocationStub implements BContent try { java.lang.reflect.Field uidField = attributionSourceClass.getDeclaredField("mUid"); uidField.setAccessible(true); - uidField.set(attributionSource, BActivityThread.getBUid()); + uidField.set(attributionSource, BlackBoxCore.getHostUid()); Slog.d(TAG, "Fixed AttributionSource UID via field access"); } catch (NoSuchFieldException e) { // Try alternative field names try { java.lang.reflect.Field uidField = attributionSourceClass.getDeclaredField("uid"); uidField.setAccessible(true); - uidField.set(attributionSource, BActivityThread.getBUid()); + uidField.set(attributionSource, BlackBoxCore.getHostUid()); Slog.d(TAG, "Fixed AttributionSource UID via alternative field"); } catch (NoSuchFieldException e2) { // Try using setter method try { java.lang.reflect.Method setUidMethod = attributionSourceClass.getDeclaredMethod("setUid", int.class); setUidMethod.setAccessible(true); - setUidMethod.invoke(attributionSource, BActivityThread.getBUid()); + setUidMethod.invoke(attributionSource, BlackBoxCore.getHostUid()); Slog.d(TAG, "Fixed AttributionSource UID via setter method"); } catch (Exception e3) { Slog.w(TAG, "Could not fix AttributionSource UID: " + e3.getMessage()); diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/context/providers/SystemProviderStub.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/context/providers/SystemProviderStub.java index ee675b8..305e3f0 100644 --- a/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/context/providers/SystemProviderStub.java +++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/service/context/providers/SystemProviderStub.java @@ -52,7 +52,25 @@ public class SystemProviderStub extends ClassInvocationStub implements BContentP if ("asBinder".equals(method.getName())) { return method.invoke(mBase, args); } - // Don't replace system provider authorities like "settings", "media", etc. + + String methodName = method.getName(); + + // For call() method, args[0] is the method name (like "GET_global"), NOT a package name + // Don't replace it! The method name is essential for Settings provider to work + if ("call".equals(methodName)) { + // Only fix AttributionSource in call() args, don't replace method name + if (args != null) { + for (int i = 0; i < args.length; i++) { + Object arg = args[i]; + if (arg != null && arg.getClass().getName().equals(BRAttributionSource.getRealClass().getName())) { + ContextCompat.fixAttributionSourceState(arg, BlackBoxCore.getHostUid()); + } + } + } + return method.invoke(mBase, args); + } + + // For other methods like query/insert/update/delete, we may need to fix package names if (args != null && args.length > 0) { Object arg = args[0]; if (arg instanceof String) { diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/AttributionSourceUtils.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/AttributionSourceUtils.java index 85e460f..ff89557 100644 --- a/Bcore/src/main/java/top/niunaijun/blackbox/utils/AttributionSourceUtils.java +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/AttributionSourceUtils.java @@ -58,7 +58,7 @@ public class AttributionSourceUtils { try { java.lang.reflect.Field uidField = attributionSourceClass.getDeclaredField(fieldName); uidField.setAccessible(true); - uidField.set(attributionSource, BActivityThread.getBUid()); + uidField.set(attributionSource, BlackBoxCore.getHostUid()); Slog.d(TAG, "Fixed AttributionSource UID via field: " + fieldName); break; } catch (NoSuchFieldException e) { @@ -70,7 +70,7 @@ public class AttributionSourceUtils { try { java.lang.reflect.Method setUidMethod = attributionSourceClass.getDeclaredMethod("setUid", int.class); setUidMethod.setAccessible(true); - setUidMethod.invoke(attributionSource, BActivityThread.getBUid()); + setUidMethod.invoke(attributionSource, BlackBoxCore.getHostUid()); Slog.d(TAG, "Fixed AttributionSource UID via setter method"); } catch (Exception e) { // Ignore setter method errors @@ -140,7 +140,7 @@ public class AttributionSourceUtils { // Try constructor with UID and package name java.lang.reflect.Constructor constructor = attributionSourceClass.getDeclaredConstructor(int.class, String.class); constructor.setAccessible(true); - attributionSource = constructor.newInstance(BActivityThread.getBUid(), BlackBoxCore.getHostPkg()); + attributionSource = constructor.newInstance(BlackBoxCore.getHostUid(), BlackBoxCore.getHostPkg()); } catch (Exception e) { try { // Try default constructor diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/StoragePermissionHelper.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/StoragePermissionHelper.java new file mode 100644 index 0000000..22c4265 --- /dev/null +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/StoragePermissionHelper.java @@ -0,0 +1,217 @@ +package top.niunaijun.blackbox.utils; + +import android.Manifest; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.provider.Settings; + +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +/** + * Helper class for managing storage permissions on Android 11+ (SDK 30+) + * Provides utilities for requesting and checking MANAGE_EXTERNAL_STORAGE permission. + * + * Created by alex5402 + */ +public class StoragePermissionHelper { + + private static final String TAG = "StoragePermissionHelper"; + + public static final int REQUEST_CODE_STORAGE_PERMISSION = 1001; + public static final int REQUEST_CODE_MANAGE_STORAGE = 1002; + + /** + * Check if the app has all files access permission (MANAGE_EXTERNAL_STORAGE on Android 11+) + */ + public static boolean hasAllFilesAccess() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + return Environment.isExternalStorageManager(); + } else { + // On older versions, regular storage permission is sufficient + return true; + } + } + + /** + * Check if the app has basic storage permissions (READ/WRITE_EXTERNAL_STORAGE) + */ + public static boolean hasStoragePermission(Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + // Android 13+ uses granular media permissions + return ContextCompat.checkSelfPermission(context, + Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED || + ContextCompat.checkSelfPermission(context, + Manifest.permission.READ_MEDIA_VIDEO) == PackageManager.PERMISSION_GRANTED || + ContextCompat.checkSelfPermission(context, + Manifest.permission.READ_MEDIA_AUDIO) == PackageManager.PERMISSION_GRANTED; + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + // Android 11-12: Check MANAGE_EXTERNAL_STORAGE or fallback to READ + return Environment.isExternalStorageManager() || + ContextCompat.checkSelfPermission(context, + Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; + } else { + // Android 10 and below + return ContextCompat.checkSelfPermission(context, + Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED && + ContextCompat.checkSelfPermission(context, + Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; + } + } + + /** + * Check if full file access is available (either granted or not needed) + */ + public static boolean hasFullFileAccess(Context context) { + return hasAllFilesAccess() && hasStoragePermission(context); + } + + /** + * Request MANAGE_EXTERNAL_STORAGE permission by opening system settings + * Call this for Android 11+ when you need all files access + */ + public static void requestAllFilesAccess(Activity activity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + if (!Environment.isExternalStorageManager()) { + try { + Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); + intent.setData(Uri.parse("package:" + activity.getPackageName())); + activity.startActivityForResult(intent, REQUEST_CODE_MANAGE_STORAGE); + } catch (Exception e) { + // Fallback: open general manage all files settings + Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION); + activity.startActivityForResult(intent, REQUEST_CODE_MANAGE_STORAGE); + } + } + } + } + + /** + * Request MANAGE_EXTERNAL_STORAGE permission with a custom package name + * Useful for requesting on behalf of sandboxed apps + */ + public static void requestAllFilesAccessForPackage(Activity activity, String packageName) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + try { + Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION); + intent.setData(Uri.parse("package:" + packageName)); + activity.startActivityForResult(intent, REQUEST_CODE_MANAGE_STORAGE); + } catch (Exception e) { + // Fallback: open general manage all files settings + Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION); + activity.startActivityForResult(intent, REQUEST_CODE_MANAGE_STORAGE); + } + } + } + + /** + * Request basic storage permissions (READ/WRITE_EXTERNAL_STORAGE) + * For Android 13+, requests granular media permissions + */ + public static void requestStoragePermission(Activity activity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + // Android 13+: Request granular media permissions + ActivityCompat.requestPermissions(activity, + new String[]{ + Manifest.permission.READ_MEDIA_IMAGES, + Manifest.permission.READ_MEDIA_VIDEO, + Manifest.permission.READ_MEDIA_AUDIO + }, + REQUEST_CODE_STORAGE_PERMISSION); + } else { + // Android 12 and below + ActivityCompat.requestPermissions(activity, + new String[]{ + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE + }, + REQUEST_CODE_STORAGE_PERMISSION); + } + } + + /** + * Request all necessary storage permissions for full file access + * This includes MANAGE_EXTERNAL_STORAGE on Android 11+ + */ + public static void requestFullFileAccess(Activity activity) { + // First request basic permissions + if (!hasStoragePermission(activity)) { + requestStoragePermission(activity); + } + + // Then request all files access if on Android 11+ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) { + requestAllFilesAccess(activity); + } + } + + /** + * Check if we need to show permission rationale for storage permission + */ + public static boolean shouldShowStorageRationale(Activity activity) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + return ActivityCompat.shouldShowRequestPermissionRationale(activity, + Manifest.permission.READ_MEDIA_IMAGES); + } else { + return ActivityCompat.shouldShowRequestPermissionRationale(activity, + Manifest.permission.READ_EXTERNAL_STORAGE); + } + } + + /** + * Get a user-friendly message about why storage permission is needed + */ + public static String getPermissionRationale() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + return "This app needs access to all files to properly manage sandboxed applications. " + + "Please grant 'All files access' permission in the settings."; + } else { + return "This app needs storage permission to properly manage sandboxed applications. " + + "Please grant storage permission."; + } + } + + /** + * Handle the result from permission request + * @return true if all required permissions are now granted + */ + public static boolean handlePermissionResult(Activity activity, int requestCode, + String[] permissions, int[] grantResults) { + if (requestCode == REQUEST_CODE_STORAGE_PERMISSION) { + // Check if any permission was granted + for (int result : grantResults) { + if (result == PackageManager.PERMISSION_GRANTED) { + Slog.d(TAG, "Storage permission granted"); + return true; + } + } + Slog.w(TAG, "Storage permission denied"); + return false; + } + return false; + } + + /** + * Handle the result from MANAGE_EXTERNAL_STORAGE settings activity + * Should be called in onActivityResult + */ + public static boolean handleAllFilesAccessResult(int requestCode) { + if (requestCode == REQUEST_CODE_MANAGE_STORAGE) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + boolean granted = Environment.isExternalStorageManager(); + if (granted) { + Slog.d(TAG, "All files access granted"); + } else { + Slog.w(TAG, "All files access denied"); + } + return granted; + } + } + return false; + } +} diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/ContextCompat.java b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/ContextCompat.java index 2c61964..5f28016 100644 --- a/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/ContextCompat.java +++ b/Bcore/src/main/java/top/niunaijun/blackbox/utils/compat/ContextCompat.java @@ -78,7 +78,9 @@ public class ContextCompat { if (BuildCompat.isS()) { try { - fixAttributionSourceState(BRContextImpl.get(context).getAttributionSource(), BlackBoxCore.getBUid()); + // Use getHostUid() instead of getBUid() - AttributionSource UID must match + // the calling process's actual UID (host UID), not the virtual UID + fixAttributionSourceState(BRContextImpl.get(context).getAttributionSource(), BlackBoxCore.getHostUid()); } catch (Exception e) { Slog.w(TAG, "Failed to fix attribution source state: " + e.getMessage()); } diff --git a/CHANGES_SUMMARY.md b/CHANGES_SUMMARY.md deleted file mode 100644 index c1cf40a..0000000 --- a/CHANGES_SUMMARY.md +++ /dev/null @@ -1,178 +0,0 @@ -# Changes Summary - Social Media App Crash Fixes - -## Files Modified - -### 1. IActivityManagerProxy.java -- **Enhanced error handling** for all ActivityManager calls -- **SecurityException handling** with safe default returns -- **Improved startActivity, startService, stopService** methods -- **Better permission handling** to prevent crashes - -### 2. WebViewProxy.java -- **Enhanced WebView constructor** with better error handling -- **Unique data directory creation** for each virtual app -- **Fallback WebView creation** when normal initialization fails -- **Improved error handling** for all WebView operations - -### 3. AttributionSourceUtils.java -- **Added validation method** for AttributionSource objects -- **Enhanced error handling** for UID fixing operations -- **Better logging** for debugging AttributionSource issues - -### 4. SimpleCrashFix.java -- **Added WebView crash detection** and prevention -- **Added AttributionSource crash detection** and prevention -- **Added social media app crash detection** and prevention -- **Enhanced global exception handler** for comprehensive crash prevention - -### 5. HookManager.java -- **Enhanced error handling** for hook failures -- **Critical hook recovery** mechanisms -- **Hook status monitoring** capabilities -- **Force re-initialization** functionality - -### 6. BlackBoxCore.java -- **Integrated social media crash prevention** initialization -- **Automatic crash prevention** setup at class loading time - -## Files Created - -### 1. SocialMediaAppCrashPrevention.java -- **Comprehensive crash prevention** specifically for social media apps -- **Automatic detection** of social media app packages -- **Specialized handling** for Facebook, Instagram, WhatsApp, etc. -- **WebView, context, and permission** crash prevention - -### 2. SOCIAL_MEDIA_CRASH_FIXES.md -- **Complete documentation** of all implemented fixes -- **Installation and usage** instructions -- **Troubleshooting guide** for common issues -- **Performance impact** analysis - -### 3. CHANGES_SUMMARY.md -- **Summary of all changes** made to fix crashes -- **Quick reference** for developers - -## Key Improvements - -### Crash Prevention -- **Global exception handler** prevents crashes from propagating -- **Specific handling** for WebView, AttributionSource, and social media app crashes -- **Graceful fallbacks** for failed operations -- **Automatic recovery** mechanisms for critical hooks - -### WebView Support -- **Unique data directories** prevent conflicts between virtual apps -- **Automatic directory creation** with proper permissions -- **Fallback WebView creation** when normal initialization fails -- **Enhanced configuration** for better compatibility - -### AttributionSource Handling -- **Comprehensive UID fixing** for Android 12+ compatibility -- **Multiple field name support** for different Android versions -- **Bundle object handling** for complex data structures -- **Safe fallback creation** when original objects fail - -### Error Handling -- **Comprehensive try-catch** blocks throughout the codebase -- **Meaningful error messages** for debugging -- **Graceful degradation** when operations fail -- **Automatic retry** mechanisms for critical operations - -### Performance Optimization -- **Minimal memory overhead** (~2-5MB additional usage) -- **Negligible CPU impact** during normal operation -- **Fast initialization** (~100-200ms additional startup time) -- **No runtime performance** impact - -## Supported Social Media Apps - -- **Facebook** & Messenger -- **Instagram** -- **WhatsApp** -- **Telegram** -- **Twitter/X** -- **TikTok** -- **Snapchat** -- **YouTube** -- **LinkedIn** -- **Discord** -- **Reddit** -- **Spotify** -- **Netflix** -- **Prime Video** - -## Android Version Support - -- **Android 5.0+ (API 21+)**: Full support -- **Android 10+ (API 29+)**: Enhanced WebView support -- **Android 12+ (API 31+)**: Full AttributionSource support - -## Device Manufacturer Support - -- **Xiaomi/MIUI**: Enhanced support with Xiaomi-specific proxies -- **Samsung**: Full support -- **Huawei**: Full support -- **OnePlus**: Full support -- **Google**: Full support - -## Testing Recommendations - -1. **Clean Installation**: Test with fresh virtual app installations -2. **Multiple Apps**: Test with different social media apps simultaneously -3. **Device Variety**: Test on different Android versions and device types -4. **Stress Testing**: Test with multiple app launches and rapid switching -5. **Log Monitoring**: Monitor logs for any remaining crash issues - -## Monitoring and Debugging - -### Log Tags to Monitor -- `SimpleCrashFix` - General crash prevention -- `SocialMediaCrashPrevention` - Social media specific fixes -- `WebViewProxy` - WebView crash prevention -- `AttributionSourceUtils` - AttributionSource fixes -- `HookManager` - Hook management and recovery - -### Status Checking Commands -```java -// Check crash prevention status -String status = SocialMediaAppCrashPrevention.getCrashPreventionStatus(); - -// Check if current app is social media -boolean isSocialMedia = SocialMediaAppCrashPrevention.isSocialMediaApp(); - -// Check hook status -HookManager hookManager = HookManager.get(); -boolean hooksOk = hookManager.areCriticalHooksInstalled(); -``` - -## Expected Results - -After implementing these fixes: - -1. **Facebook and Instagram** should no longer crash with black screens -2. **WebView-based content** should load properly in social media apps -3. **App startup** should be more stable and reliable -4. **Permission-related crashes** should be eliminated -5. **AttributionSource errors** should be resolved on Android 12+ -6. **Overall app stability** should be significantly improved - -## Future Enhancements - -1. **Machine Learning Crash Prediction** - Predict crashes before they happen -2. **Dynamic Hook Optimization** - Optimize hooks based on app behavior -3. **Enhanced Xiaomi Support** - Better MIUI compatibility -4. **Performance Profiling** - Detailed performance impact analysis -5. **Automated Testing** - Automated crash prevention testing - -## Conclusion - -These comprehensive fixes address the root causes of social media app crashes in the BlackBox virtual environment. The implementation provides: - -- **Robust crash prevention** mechanisms -- **Comprehensive error handling** throughout the system -- **Device-specific optimizations** for better compatibility -- **Performance-conscious** implementation with minimal overhead -- **Easy monitoring and debugging** capabilities - -The fixes should resolve the black screen and crash issues while maintaining system stability and performance. diff --git a/COMPILATION_ERRORS_FIXED.md b/COMPILATION_ERRORS_FIXED.md deleted file mode 100644 index 8d1d12c..0000000 --- a/COMPILATION_ERRORS_FIXED.md +++ /dev/null @@ -1,146 +0,0 @@ -# Compilation Errors Fixed - -## Issues Identified - -The build was failing with several compilation errors in `BPackageManager.java`: - -### 1. Private Access Errors -``` -error: mService has private access in BlackManager -``` -- **Problem**: Trying to access the private `mService` field directly -- **Solution**: Removed the duplicate `isServiceHealthy()` method since it already exists in the parent `BlackManager` class - -### 2. Invalid Android API Fields -``` -error: cannot find symbol -symbol: variable publicDataDir -symbol: variable publicNativeLibraryDir -symbol: variable sharedUserId -symbol: variable sharedUserLabel -symbol: variable size -symbol: variable seinfo -symbol: variable permission -``` -- **Problem**: Using fields that don't exist in the target Android API version -- **Solution**: Removed invalid fields from fallback methods - -### 3. Type Mismatch Errors -``` -error: incompatible types: String[] cannot be converted to String -error: incompatible types: String cannot be converted to int -``` -- **Problem**: Incorrect data types for certain fields -- **Solution**: Fixed field assignments to use correct types - -## Fixes Applied - -### 1. Removed Duplicate Method -```java -// REMOVED: Duplicate isServiceHealthy() method -// The parent BlackManager class already provides this method -``` - -### 2. Fixed ApplicationInfo Fallback Method -```java -private ApplicationInfo createFallbackApplicationInfo(String packageName, int flags, int userId) { - Log.w(TAG, "Creating fallback ApplicationInfo for " + packageName); - ApplicationInfo info = new ApplicationInfo(); - info.packageName = packageName; - info.flags = flags; - info.uid = 0; // Placeholder - info.sourceDir = "/system/app/" + packageName + ".apk"; // Placeholder - info.dataDir = "/data/data/" + packageName; // Placeholder - info.nativeLibraryDir = "/data/app-lib/" + packageName; // Placeholder - info.publicSourceDir = "/system/app/" + packageName + ".apk"; // Placeholder - info.metaData = new Bundle(); // Placeholder - info.splitNames = new String[]{}; // Placeholder - return info; -} -``` - -### 3. Fixed PackageInfo Fallback Method -```java -private PackageInfo createFallbackPackageInfo(String packageName, int flags, int userId) { - Log.w(TAG, "Creating fallback PackageInfo for " + packageName); - PackageInfo info = new PackageInfo(); - info.packageName = packageName; - info.versionCode = 1; // Placeholder - info.versionName = "1.0"; // Placeholder - info.applicationInfo = createFallbackApplicationInfo(packageName, flags, userId); - info.firstInstallTime = System.currentTimeMillis(); // Placeholder - info.lastUpdateTime = System.currentTimeMillis(); // Placeholder - info.installLocation = 0; // Placeholder - info.gids = new int[]{}; // Placeholder - info.splitNames = new String[]{}; // Placeholder - info.signatures = new Signature[]{}; // Placeholder - return info; -} -``` - -## Removed Invalid Fields - -The following fields were removed because they don't exist in the target Android API: - -### ApplicationInfo (Removed) -- `publicDataDir` -- `publicNativeLibraryDir` -- `permission` (String[] type) -- `sharedUserId` -- `sharedUserLabel` - -### PackageInfo (Removed) -- `size` -- `seinfo` -- `permission` (String[] type) -- `sharedUserId` -- `sharedUserLabel` - -## Valid Fields Used - -### ApplicationInfo (Valid) -- `packageName` -- `flags` -- `uid` -- `sourceDir` -- `dataDir` -- `nativeLibraryDir` -- `publicSourceDir` -- `metaData` -- `splitNames` - -### PackageInfo (Valid) -- `packageName` -- `versionCode` -- `versionName` -- `applicationInfo` -- `firstInstallTime` -- `lastUpdateTime` -- `installLocation` -- `gids` -- `splitNames` -- `signatures` - -## Result - -After applying these fixes: - -✅ **All compilation errors resolved** -✅ **Valid Android API fields only** -✅ **Proper inheritance from BlackManager** -✅ **Fallback methods work correctly** -✅ **Type safety maintained** - -## Build Status - -The project should now compile successfully without any errors. The fallback mechanisms will work properly when the PackageManager service is unavailable, providing basic functionality while preventing crashes. - -## Testing - -1. **Build the project** in Android Studio -2. **Verify no compilation errors** -3. **Test app startup** on target devices -4. **Monitor logcat** for fallback usage -5. **Verify app functionality** with limited services - -The fixes maintain the core functionality while ensuring compatibility with the target Android API version. diff --git a/COMPILATION_ERROR_FIX.md b/COMPILATION_ERROR_FIX.md deleted file mode 100644 index 67e0ac3..0000000 --- a/COMPILATION_ERROR_FIX.md +++ /dev/null @@ -1,111 +0,0 @@ -# Compilation Error Fix - Missing processName Variable - -## Problem Analysis - -The build was failing with a compilation error: - -``` -error: cannot find symbol -symbol: variable processName -location: class BActivityThread -``` - -**Root Cause:** The `processName` variable was not available in the scope where it was being used in the `createApplicationWithFallback` method. - -## Issue Details - -### Location of Error -- **File**: `Bcore/src/main/java/top/niunaijun/blackbox/app/BActivityThread.java` -- **Line**: 476 -- **Method**: `createApplicationWithFallback` - -### Method Signature -```java -private Application createApplicationWithFallback(Object loadedApk, Context packageContext, String packageName) -``` - -### Available Parameters -- `loadedApk` - Object -- `packageContext` - Context -- `packageName` - String - -### Missing Variable -- `processName` - Not available in this method scope - -## Fix Applied - -### Before (Error) -```java -// Method 4: Create a minimal application wrapper -try { - application = createMinimalApplication(packageName, processName); // ERROR: processName not available - if (application != null) { - Slog.d(TAG, "Successfully created minimal application wrapper"); - return application; - } -} catch (Exception e) { - lastException = e; - Slog.w(TAG, "Minimal application creation failed: " + e.getMessage()); -} -``` - -### After (Fixed) -```java -// Method 4: Create a minimal application wrapper -try { - application = createMinimalApplication(packageName, packageName); // Use packageName as processName - if (application != null) { - Slog.d(TAG, "Successfully created minimal application wrapper"); - return application; - } -} catch (Exception e) { - lastException = e; - Slog.w(TAG, "Minimal application creation failed: " + e.getMessage()); -} -``` - -## Solution Explanation - -### Why This Works -1. **Package Name as Process Name**: In most cases, the package name and process name are the same for the main application process -2. **Fallback Strategy**: This is part of a fallback mechanism, so using the package name as the process name is acceptable -3. **Minimal Application**: The `createMinimalApplication` method creates a basic application that doesn't require the exact process name - -### Alternative Solutions Considered -1. **Add processName parameter**: Would require changing the method signature and all call sites -2. **Create separate method**: Would duplicate code unnecessarily -3. **Use null**: Could cause issues in the minimal application creation - -### Chosen Solution -Using `packageName` as the `processName` is the most practical solution because: -- It maintains the existing method signature -- It's a reasonable fallback for process name -- It doesn't require extensive code changes -- It's safe for the minimal application creation context - -## Expected Results - -After applying this fix: - -✅ **Compilation error resolved** -✅ **Build completes successfully** -✅ **Fallback mechanism works correctly** -✅ **Minimal application creation functions properly** -✅ **No impact on existing functionality** - -## Testing - -1. **Build the project** in Android Studio -2. **Verify no compilation errors** -3. **Test app startup** on target devices -4. **Monitor logcat** for minimal application creation messages -5. **Verify fallback mechanisms** work correctly - -## Additional Notes - -- This fix is part of the larger APK path handling improvements -- The minimal application creation is a fallback mechanism for when normal application creation fails -- Using package name as process name is a common practice in Android development -- The fix maintains backward compatibility and doesn't affect existing functionality - -The compilation error has been resolved and the project should now build successfully. diff --git a/COMPILATION_FIX_SUMMARY.md b/COMPILATION_FIX_SUMMARY.md deleted file mode 100644 index a64845a..0000000 --- a/COMPILATION_FIX_SUMMARY.md +++ /dev/null @@ -1,69 +0,0 @@ -# Compilation Fix Summary for ClassLoaderProxy - -## Issue Identified - -The `ClassLoaderProxy.java` file had a compilation error in the `LoadDexFile` inner class: - -``` -error: cannot find symbol - Field pathListField = who.getClass().getDeclaredField("pathList"); - ^ -symbol: variable who -location: class LoadDexFile -``` - -## Root Cause - -The `extractCorruptedFilePathFromError` method was trying to access the `who` parameter, but it was defined as a private method without receiving the `who` parameter from the calling context. - -## Fix Applied - -### Before (Broken Code) -```java -private String extractCorruptedFilePathFromError(Exception e) { - // ... code ... - Field pathListField = who.getClass().getDeclaredField("pathList"); // ERROR: who not in scope - // ... code ... -} -``` - -### After (Fixed Code) -```java -private String extractCorruptedFilePathFromError(Exception e, Object who) { - // ... code ... - Field pathListField = who.getClass().getDeclaredField("pathList"); // FIXED: who parameter received - // ... code ... -} -``` - -## Changes Made - -1. **Method Signature Update**: Added `Object who` parameter to `extractCorruptedFilePathFromError` -2. **Method Call Update**: Updated the call to pass the `who` parameter: `extractCorruptedFilePathFromError(e, who)` - -## Code Location - -**File**: `Bcore/src/main/java/top/niunaijun/blackbox/fake/service/ClassLoaderProxy.java` -**Lines**: 287 and 300 - -## Technical Details - -The `LoadDexFile` class is a static inner class that extends `MethodHook`. When the `hook` method is called, it receives the `who` parameter, but the private helper method `extractCorruptedFilePathFromError` was not receiving this parameter. - -By adding the `who` parameter to the method signature and updating the method call, the compilation error is resolved and the DEX file recovery functionality can work properly. - -## Verification - -The fix ensures that: -- ✅ The `who` parameter is properly accessible in the helper method -- ✅ DEX file corruption detection can work correctly -- ✅ Reflection-based path extraction can access the DexPathList object -- ✅ The ClassLoader proxy can properly recover from DEX file corruption - -## Impact - -This fix enables the comprehensive DEX file corruption recovery system to work properly, which is essential for preventing the `"classes.dex: Entry not found"` crashes that were causing Facebook, Instagram, and other social media apps to fail with black screens. - -## Status - -✅ **COMPILATION ERROR FIXED** - The ClassLoaderProxy should now compile successfully and provide full DEX file corruption recovery functionality. diff --git a/COMPREHENSIVE_CRASH_FIXES_SUMMARY.md b/COMPREHENSIVE_CRASH_FIXES_SUMMARY.md deleted file mode 100644 index 08ee377..0000000 --- a/COMPREHENSIVE_CRASH_FIXES_SUMMARY.md +++ /dev/null @@ -1,380 +0,0 @@ -# Comprehensive Crash Fixes Summary for Bcore - -## Overview - -This document provides a complete summary of all the crash fixes implemented to resolve the black screen and crash issues in social media apps like Facebook, Instagram, and WhatsApp when running in the BlackBox virtual environment. - -## Root Cause Analysis - -Based on comprehensive logcat analysis, the crashes were caused by multiple interrelated issues: - -### 1. Primary Issue: DEX File Corruption -- **Error**: `"classes.dex: Entry not found"` -- **Impact**: ClassNotFoundException crashes preventing app startup -- **Apps Affected**: Instagram, Facebook, WhatsApp, and other social media apps - -### 2. Secondary Issues -- **WebView Data Directory Conflicts**: Multiple virtual apps sharing WebView data -- **AttributionSource UID Mismatches**: Android 12+ security enforcement issues -- **Context and Resource Access Issues**: Null context crashes during initialization -- **Xiaomi-specific Security Enforcement**: Strict UID validation on MIUI devices - -## Implemented Solutions - -### 1. DEX File Corruption Fixes (Primary Solution) - -#### Enhanced ClassLoader Proxy -- **File**: `ClassLoaderProxy.java` -- **Purpose**: Handle DEX file corruption and ClassLoader failures -- **Features**: - - Comprehensive error handling for all ClassLoader operations - - Multiple fallback ClassLoader mechanisms - - Class caching to avoid repeated failures - - DEX file validation before class loading - -#### DEX File Recovery Utility -- **File**: `DexFileRecovery.java` -- **Purpose**: Recover corrupted DEX files using multiple strategies -- **Recovery Methods**: - 1. Alternative APK files (Priority: 100) - 2. Host app APK fallback (Priority: 80) - 3. System APK files (Priority: 60) - 4. DEX extraction from corrupted APKs (Priority: 40) - 5. Backup file restoration (Priority: 20) - -#### DEX Crash Prevention -- **File**: `DexCrashPrevention.java` -- **Purpose**: Proactively prevent DEX file corruption -- **Features**: - - APK file monitoring and validation - - Split APK conflict resolution - - Proactive corruption detection - - Integrity checks before runtime - -### 2. WebView Crash Prevention - -#### Enhanced WebView Proxy -- **File**: `WebViewProxy.java` -- **Purpose**: Prevent WebView data directory conflicts -- **Features**: - - Unique data directories for each virtual app - - Automatic directory creation with proper permissions - - Fallback WebView creation when initialization fails - - Enhanced error handling for all WebView operations - -### 3. AttributionSource UID Fixes - -#### Enhanced AttributionSource Utils -- **File**: `AttributionSourceUtils.java` -- **Purpose**: Fix UID mismatches for Android 12+ compatibility -- **Features**: - - Comprehensive UID fixing for AttributionSource objects - - Multiple field name and setter method support - - Bundle object handling for complex data structures - - Safe fallback AttributionSource creation - -### 4. Context and Resource Access Fixes - -#### Enhanced SimpleCrashFix -- **File**: `SimpleCrashFix.java` -- **Purpose**: Global crash prevention for all types of crashes -- **Features**: - - Global exception handler for comprehensive crash prevention - - Specific handling for WebView, AttributionSource, and social media app crashes - - Context wrapper hooks to prevent null context issues - - Google Play Services crash prevention - -#### Context Wrapper Hook -- **File**: `ContextWrapperHook.java` -- **Purpose**: Prevent null context crashes -- **Features**: - - Direct hooking of ContextWrapper.getResources() - - Graceful handling of null contexts - - Fallback to host context resources - -### 5. Social Media App Specific Fixes - -#### Social Media App Crash Prevention -- **File**: `SocialMediaAppCrashPrevention.java` -- **Purpose**: Specialized crash prevention for social media apps -- **Features**: - - Automatic detection of social media app packages - - Specialized handling for Facebook, Instagram, WhatsApp, etc. - - WebView, context, and permission crash prevention - - Comprehensive monitoring and debugging - -### 6. System Service Fixes - -#### Enhanced IActivityManager Proxy -- **File**: `IActivityManagerProxy.java` -- **Purpose**: Prevent ActivityManager-related crashes -- **Features**: - - Comprehensive error handling for all ActivityManager calls - - SecurityException handling with safe default returns - - Enhanced permission handling for social media apps - - Graceful fallback for failed operations - -#### Enhanced Hook Manager -- **File**: `HookManager.java` -- **Purpose**: Better hook management and recovery -- **Features**: - - Enhanced error handling for hook failures - - Critical hook recovery mechanisms - - Hook status monitoring and re-initialization - - Force re-initialization capabilities - -## Integration Architecture - -### BlackBoxCore Integration -```java -static { - try { - // Install all crash prevention mechanisms at class loading time - SimpleCrashFix.installSimpleFix(); - StackTraceFilter.install(); - SocialMediaAppCrashPrevention.initialize(); - DexCrashPrevention.initialize(); - } catch (Exception e) { - Slog.w(TAG, "Failed to install crash prevention: " + e.getMessage()); - } -} -``` - -### Hook Registration -All enhanced proxies are automatically registered in the HookManager: -```java -addInjector(new ClassLoaderProxy()); // Enhanced with DEX recovery -addInjector(new WebViewProxy()); // Enhanced with data directory handling -addInjector(new IActivityManagerProxy()); // Enhanced with error handling -addInjector(new IContentProviderProxy()); // Enhanced with AttributionSource fixes -``` - -## Recovery Flow - -### 1. Detection Phase -- **DEX Corruption**: ClassNotFoundException and "classes.dex: Entry not found" errors -- **WebView Issues**: Data directory conflicts and initialization failures -- **AttributionSource**: UID mismatch errors on Android 12+ -- **Context Issues**: Null context crashes during app initialization - -### 2. Recovery Phase -- **Primary Recovery**: Try alternative APK files and ClassLoaders -- **Secondary Recovery**: Use host app resources as fallback -- **Tertiary Recovery**: Extract and repair corrupted files -- **Fallback Recovery**: Use system resources when available - -### 3. Prevention Phase -- **Caching**: Store successful operations to avoid repeated failures -- **Validation**: Pre-validate files and resources before use -- **Monitoring**: Continuous monitoring for potential issues -- **Statistics**: Track recovery success rates and performance - -## Performance Impact - -### Memory Usage -- **Total Additional Memory**: ~6-12MB -- **Class Cache**: ~2-5MB -- **Recovery Cache**: ~1-2MB -- **Prevention Cache**: ~1-2MB -- **WebView Cache**: ~1-2MB -- **Other Utilities**: ~1MB - -### CPU Impact -- **Normal Operation**: Negligible overhead -- **Recovery Operations**: Only when issues are detected -- **Validation**: Minimal background processing -- **Monitoring**: Low-priority background tasks - -### Startup Time -- **Total Additional Time**: ~200-400ms -- **Initialization**: ~50-100ms -- **Pre-validation**: ~100-200ms -- **Hook Setup**: ~50-100ms - -## Compatibility Matrix - -### Android Versions -| Version | API Level | DEX Recovery | WebView | AttributionSource | Overall Support | -|---------|-----------|--------------|---------|-------------------|-----------------| -| Android 5.0+ | 21+ | ✅ Full | ✅ Full | ✅ Full | ✅ Full | -| Android 10+ | 29+ | ✅ Enhanced | ✅ Enhanced | ✅ Full | ✅ Enhanced | -| Android 12+ | 31+ | ✅ Full | ✅ Full | ✅ Enhanced | ✅ Full | - -### Device Manufacturers -| Manufacturer | DEX Recovery | WebView | AttributionSource | MIUI Support | -|--------------|--------------|---------|-------------------|--------------| -| Samsung | ✅ Full | ✅ Full | ✅ Full | ✅ Full | -| Xiaomi/MIUI | ✅ Full | ✅ Full | ✅ Full | ✅ Enhanced | -| Huawei | ✅ Full | ✅ Full | ✅ Full | ✅ Full | -| OnePlus | ✅ Full | ✅ Full | ✅ Full | ✅ Full | -| Google | ✅ Full | ✅ Full | ✅ Full | ✅ Full | - -### App Categories -| Category | DEX Issues | WebView Issues | AttributionSource | Overall Fix | -|----------|------------|----------------|-------------------|-------------| -| Social Media | ✅ Fixed | ✅ Fixed | ✅ Fixed | ✅ 100% Fixed | -| Web Apps | ✅ Fixed | ✅ Fixed | ✅ Fixed | ✅ 100% Fixed | -| Games | ✅ Fixed | ✅ Fixed | ✅ Fixed | ✅ 100% Fixed | -| System Apps | ✅ Fixed | ✅ Fixed | ✅ Fixed | ✅ 100% Fixed | - -## Testing Results - -### Before Fixes -- **Instagram**: 100% crash rate with black screen -- **Facebook**: 100% crash rate with ClassNotFoundException -- **WhatsApp**: 100% crash rate during initialization -- **Overall**: 100% failure rate for social media apps - -### After Fixes -- **Instagram**: 0% crash rate, loads successfully -- **Facebook**: 0% crash rate, starts normally -- **WhatsApp**: 0% crash rate, initializes properly -- **Overall**: 0% failure rate for social media apps - -## Monitoring and Debugging - -### Log Tags -- `ClassLoaderProxy` - ClassLoader operations and DEX recovery -- `DexFileRecovery` - DEX file recovery attempts -- `DexCrashPrevention` - Prevention mechanisms and validation -- `WebViewProxy` - WebView crash prevention -- `AttributionSourceUtils` - AttributionSource fixes -- `SocialMediaCrashPrevention` - Social media specific fixes -- `SimpleCrashFix` - General crash prevention - -### Status Commands -```java -// Get comprehensive status for all systems -String classLoaderStats = ClassLoaderProxy.getCacheStats(); -String recoveryStats = DexFileRecovery.getRecoveryStats(); -String preventionStats = DexCrashPrevention.getStatus(); -String webViewStats = WebViewProxy.getStatus(); -String socialMediaStats = SocialMediaAppCrashPrevention.getCrashPreventionStatus(); - -Slog.d(TAG, "ClassLoader: " + classLoaderStats); -Slog.d(TAG, "Recovery: " + recoveryStats); -Slog.d(TAG, "Prevention: " + preventionStats); -Slog.d(TAG, "WebView: " + webViewStats); -Slog.d(TAG, "Social Media: " + socialMediaStats); -``` - -### Debug Commands -```bash -# Check logs for all crash prevention systems -adb logcat | grep -E "(ClassLoaderProxy|DexFileRecovery|DexCrashPrevention|WebViewProxy|AttributionSourceUtils|SocialMediaCrashPrevention|SimpleCrashFix)" - -# Check APK files and DEX recovery -adb shell ls -la /data/app/*/*.apk -adb shell ls -la /data/data/*/cache/dex_recovery/ - -# Check WebView directories -adb shell ls -la /data/data/*/webview_* - -# Check system properties -adb shell getprop | grep -E "(webview|dex|apk)" -``` - -## Troubleshooting Guide - -### Common Issues and Solutions - -#### 1. DEX Recovery Still Failing -**Symptoms**: ClassNotFoundException persists -**Solutions**: -- Check if all recovery strategies are initialized -- Verify APK file permissions and integrity -- Clear recovery caches and retry -- Check logcat for specific error messages - -#### 2. WebView Still Crashing -**Symptoms**: WebView initialization failures -**Solutions**: -- Verify WebViewProxy is properly installed -- Check data directory permissions -- Clear WebView caches -- Verify unique directory creation - -#### 3. AttributionSource Errors Persist -**Symptoms**: UID mismatch errors on Android 12+ -**Solutions**: -- Ensure AttributionSourceUtils is working -- Check if UID is properly set -- Verify package name is correct -- Clear attribution source caches - -#### 4. Performance Issues -**Symptoms**: Slow app startup or high memory usage -**Solutions**: -- Clear all caches to reset state -- Check if too many fallback attempts -- Monitor memory usage and cache sizes -- Optimize validation frequency - -### Debug Steps - -1. **Check System Status** - ```java - String status = DexCrashPrevention.getStatus(); - String webViewStatus = WebViewProxy.getStatus(); - String socialMediaStatus = SocialMediaAppCrashPrevention.getCrashPreventionStatus(); - ``` - -2. **Force Recovery Attempts** - ```java - DexFileRecovery.RecoveryResult result = DexFileRecovery.forceRecovery(apkPath); - WebViewProxy.forceReinitialization(); - ``` - -3. **Validate Specific Components** - ```java - boolean isValid = DexFileRecovery.isValidApkFile(new File(apkPath)); - boolean isSocialMedia = SocialMediaAppCrashPrevention.isSocialMediaApp(); - ``` - -## Future Enhancements - -### Planned Improvements - -1. **Machine Learning Integration** - - Predict crashes before they occur - - Pattern recognition for common failure types - - Automated prevention strategy optimization - -2. **Enhanced Recovery Methods** - - Network-based APK recovery - - Cloud-based DEX validation - - Peer-to-peer recovery mechanisms - -3. **Performance Optimization** - - Lazy loading of recovery strategies - - Intelligent caching algorithms - - Background validation optimization - -4. **Advanced Monitoring** - - Real-time crash prediction - - Performance impact analysis - - Automated testing and validation - -## Conclusion - -These comprehensive crash fixes provide: - -- **100% Crash Prevention**: Eliminates all identified crash scenarios -- **Robust Recovery**: Multiple fallback strategies for any failure scenario -- **Proactive Prevention**: Detects and prevents issues before they cause crashes -- **Performance Optimized**: Minimal impact on app performance and startup time -- **Universal Compatibility**: Works across all Android versions and device manufacturers - -The implementation addresses the root causes of social media app crashes while maintaining system stability and performance. The multi-layered approach ensures that even if one recovery method fails, others will succeed, providing robust protection against all types of crash scenarios. - -### Key Benefits - -✅ **Social Media Apps Work**: Facebook, Instagram, WhatsApp, etc. load successfully -✅ **No More Black Screens**: Apps display content properly -✅ **Stable App Startup**: Reliable app initialization without crashes -✅ **WebView Support**: Web content loads properly in all apps -✅ **Android 12+ Compatibility**: Full support for latest Android versions -✅ **Device Compatibility**: Works on all device manufacturers -✅ **Performance Maintained**: Minimal impact on app performance - -The fixes are automatically installed when BlackBoxCore initializes and provide comprehensive crash prevention while maintaining compatibility across all Android versions and device types. diff --git a/CONTEXT_CREATION_FIX.md b/CONTEXT_CREATION_FIX.md deleted file mode 100644 index 83234fb..0000000 --- a/CONTEXT_CREATION_FIX.md +++ /dev/null @@ -1,317 +0,0 @@ -# Context Creation Fix for NullPointerException - -## Problem Analysis - -The app was crashing with a `NullPointerException` when trying to call `getResources()` on a null context: - -``` -java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference -at android.content.ContextWrapper.getResources(ContextWrapper.java:127) -at android.app.servertransaction.ClientTransactionListenerController.onContextConfigurationPreChanged(ClientTransactionListenerController.java:197) -``` - -**Root Cause:** The package context creation was failing and returning null, which caused the activity to receive a null context, leading to the NullPointerException when the system tried to access resources. - -## Issues Identified - -1. **Null Context Return**: Package context creation methods could return null -2. **Insufficient Fallback**: No proper fallback when context creation failed -3. **Activity Context Issues**: Activities receiving null contexts -4. **Resource Access Failures**: System unable to access resources from null context -5. **Incomplete Context Wrapping**: No proper context wrapper for failed cases - -## Fixes Implemented - -### 1. Enhanced Package Context Creation (`BActivityThread.java`) - -**Improved `createPackageContext` to never return null:** - -```java -public static Context createPackageContext(ApplicationInfo info) { - try { - // Check if the ApplicationInfo has a valid sourceDir - if (info.sourceDir == null) { - Slog.w(TAG, "ApplicationInfo has null sourceDir for " + info.packageName + ", using minimal context"); - return createMinimalPackageContext(info); - } - - // First, try to create the package context normally - return BlackBoxCore.getContext().createPackageContext(info.packageName, - Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); - } catch (SecurityException se) { - Slog.e(TAG, "SecurityException creating package context for " + info.packageName + ": " + se.getMessage()); - // Try alternative approach for sandboxed environments - try { - return BlackBoxCore.getContext().createPackageContext(info.packageName, - Context.CONTEXT_INCLUDE_CODE); - } catch (Exception e2) { - Slog.e(TAG, "Alternative package context creation also failed: " + e2.getMessage()); - } - } catch (Exception e) { - Slog.e(TAG, "Error creating package context for " + info.packageName + ": " + e.getMessage()); - - // If the error is related to missing APK, try to create a minimal context - if (e.getMessage() != null && e.getMessage().contains("not found")) { - Slog.w(TAG, "Package not found, attempting to create minimal context for " + info.packageName); - return createMinimalPackageContext(info); - } - } - - // If all else fails, return a minimal context to prevent null pointer exceptions - Slog.w(TAG, "All package context creation methods failed for " + info.packageName + ", using minimal context as fallback"); - return createMinimalPackageContext(info); -} -``` - -### 2. Enhanced Minimal Package Context (`BActivityThread.java`) - -**Improved `createMinimalPackageContext` with multiple strategies:** - -```java -private static Context createMinimalPackageContext(ApplicationInfo info) { - try { - // Create a context that doesn't require the actual APK - Context baseContext = BlackBoxCore.getContext(); - - // Try to create a context with minimal flags - try { - Context packageContext = baseContext.createPackageContext(info.packageName, 0); - if (packageContext != null) { - Slog.d(TAG, "Successfully created package context with minimal flags for " + info.packageName); - return packageContext; - } - } catch (Exception e) { - Slog.w(TAG, "Failed to create package context with minimal flags for " + info.packageName + ": " + e.getMessage()); - } - - // Try to create a context without any flags - try { - Context packageContext = baseContext.createPackageContext(info.packageName, Context.CONTEXT_IGNORE_SECURITY); - if (packageContext != null) { - Slog.d(TAG, "Successfully created package context with ignore security for " + info.packageName); - return packageContext; - } - } catch (Exception e) { - Slog.w(TAG, "Failed to create package context with ignore security for " + info.packageName + ": " + e.getMessage()); - } - - // Try to create a context with just the package name - try { - Context packageContext = baseContext.createPackageContext(info.packageName, Context.CONTEXT_INCLUDE_CODE); - if (packageContext != null) { - Slog.d(TAG, "Successfully created package context with include code for " + info.packageName); - return packageContext; - } - } catch (Exception e) { - Slog.w(TAG, "Failed to create package context with include code for " + info.packageName + ": " + e.getMessage()); - } - - } catch (Exception e) { - Slog.e(TAG, "Failed to create minimal package context for " + info.packageName + ": " + e.getMessage()); - } - - // Last resort: return the base context with package name wrapper - Slog.w(TAG, "Using base context as fallback for " + info.packageName); - return createWrappedBaseContext(info.packageName); -} -``` - -### 3. Context Wrapper Creation (`BActivityThread.java`) - -**Added `createWrappedBaseContext` method for ultimate fallback:** - -```java -private static Context createWrappedBaseContext(String packageName) { - try { - Context baseContext = BlackBoxCore.getContext(); - - // Create a wrapper context that provides the package name - return new ContextWrapper(baseContext) { - @Override - public String getPackageName() { - return packageName; - } - - @Override - public PackageManager getPackageManager() { - return baseContext.getPackageManager(); - } - - @Override - public Resources getResources() { - return baseContext.getResources(); - } - - @Override - public ClassLoader getClassLoader() { - return baseContext.getClassLoader(); - } - - @Override - public Context getApplicationContext() { - return baseContext.getApplicationContext(); - } - }; - } catch (Exception e) { - Slog.e(TAG, "Failed to create wrapped base context for " + packageName + ": " + e.getMessage()); - // Ultimate fallback: return the base context - return BlackBoxCore.getContext(); - } -} -``` - -### 4. Enhanced Application Binding (`BActivityThread.java`) - -**Improved `handleBindApplication` to handle context failures:** - -```java -Context packageContext = createPackageContext(applicationInfo); -if (packageContext == null) { - Slog.e(TAG, "Failed to create package context for " + packageName); - - // Try to create a minimal application without package context - Slog.w(TAG, "Attempting to create minimal application for " + packageName); - try { - Application minimalApp = createMinimalApplication(packageName, processName); - if (minimalApp != null) { - Slog.d(TAG, "Successfully created minimal application for " + packageName); - mInitialApplication = minimalApp; - BRActivityThread.get(BlackBoxCore.mainThread())._set_mInitialApplication(mInitialApplication); - - // Skip the rest of the binding process for minimal app - onBeforeApplicationOnCreate(packageName, processName, minimalApp); - AppInstrumentation.get().callApplicationOnCreate(minimalApp); - onAfterApplicationOnCreate(packageName, processName, minimalApp); - return; - } - } catch (Exception e) { - Slog.e(TAG, "Failed to create minimal application for " + packageName, e); - } - - // If we still don't have a context, create a basic one - Slog.w(TAG, "Creating basic context for " + packageName); - packageContext = createWrappedBaseContext(packageName); - if (packageContext == null) { - Slog.e(TAG, "Failed to create any context for " + packageName); - throw new RuntimeException("Unable to create any context for " + packageName); - } -} -``` - -### 5. Enhanced Minimal Application Creation (`BActivityThread.java`) - -**Improved `createMinimalApplication` with proper context attachment:** - -```java -private Application createMinimalApplication(String packageName, String processName) { - try { - Slog.d(TAG, "Creating minimal application for " + packageName); - - // Create a basic Application object - Application app = new Application() { - @Override - public void onCreate() { - super.onCreate(); - Slog.d(TAG, "Minimal application onCreate called for " + packageName); - } - - @Override - public String getPackageName() { - return packageName; - } - - @Override - public Context getApplicationContext() { - return this; - } - }; - - // Set up basic context - try { - // Use reflection to set the base context - Method attachBaseContext = Application.class.getDeclaredMethod("attachBaseContext", Context.class); - attachBaseContext.setAccessible(true); - - // Create a valid base context - Context baseContext = createWrappedBaseContext(packageName); - if (baseContext != null) { - attachBaseContext.invoke(app, baseContext); - Slog.d(TAG, "Successfully attached base context to minimal application for " + packageName); - } else { - Slog.w(TAG, "Could not create base context for minimal application: " + packageName); - } - } catch (Exception e) { - Slog.w(TAG, "Could not attach base context to minimal application: " + e.getMessage()); - } - - return app; - } catch (Exception e) { - Slog.e(TAG, "Error creating minimal application for " + packageName, e); - return null; - } -} -``` - -## Key Changes Made - -### 1. **Never Return Null** -- All context creation methods now have fallbacks -- Ultimate fallback to base context with wrapper -- Comprehensive error handling - -### 2. **Multiple Context Creation Strategies** -- Try different context creation flags -- Multiple fallback mechanisms -- Context wrapper for failed cases - -### 3. **Enhanced Application Creation** -- Proper context attachment to applications -- Minimal application with valid context -- Override key methods for package name and context - -### 4. **Comprehensive Error Handling** -- Check for null contexts at every step -- Multiple fallback strategies -- Detailed logging for debugging - -### 5. **Context Wrapper Implementation** -- Custom ContextWrapper for failed cases -- Proper delegation to base context -- Package name override for activities - -## Expected Results - -After implementing these fixes, the app should: - -✅ **No more NullPointerException** from null contexts -✅ **Always provide valid contexts** for activities -✅ **Proper resource access** through context wrappers -✅ **Graceful degradation** when package contexts fail -✅ **Detailed logging** for context creation issues -✅ **Stable activity launching** with valid contexts - -## Testing Recommendations - -1. **Test app startup** on various devices -2. **Monitor logcat** for context creation messages -3. **Test with missing APKs** to verify fallback contexts -4. **Test activity launching** to ensure no null context errors -5. **Verify resource access** works correctly -6. **Test with different Android versions** - -## Additional Notes - -- The fix ensures no context is ever null -- Context wrappers provide proper delegation -- Multiple fallback strategies prevent failures -- Enhanced logging helps with debugging -- The solution is backward compatible - -## Build Instructions - -1. Clean and rebuild the project in Android Studio -2. Test app startup on the target device -3. Monitor logcat for context creation messages -4. Verify that no NullPointerException occurs - -The comprehensive context creation fix implemented should resolve the NullPointerException crashes and ensure that activities always receive valid contexts. diff --git a/DAEMON_SERVICE_FIXES.md b/DAEMON_SERVICE_FIXES.md deleted file mode 100644 index 12b1be6..0000000 --- a/DAEMON_SERVICE_FIXES.md +++ /dev/null @@ -1,541 +0,0 @@ -# DaemonService Android 14+ Compatibility Fixes - -## Problem Description - -The Bcore DaemonService was experiencing several critical issues that prevented proper operation on Android 14+ devices: - -### 1. AndroidManifest.xml Issues -- `DaemonService` was missing the required `android:foregroundServiceType` attribute -- This attribute is mandatory for Android 14+ when starting foreground services -- Both `DaemonService` and `DaemonInnerService` were affected - -### 2. Service Implementation Problems -- `startForeground()` was called without proper notification setup -- Missing notification channel creation for Android 8.0+ -- Insufficient error handling during service startup -- No fallback mechanisms for startup failures - -### 3. Service Startup Logic Issues -- No handling for `MissingForegroundServiceTypeException` -- No fallback to regular service when foreground service fails -- Insufficient error logging and debugging information - -### 4. Process State Issues (NEW) -- **"Process is bad" errors**: System rejecting service startup due to invalid process state -- **Service startup failures**: Services failing to start even with fallback mechanisms -- **BlackBox sometimes doesn't open**: App initialization failing due to service startup issues - -### 5. ANR and Performance Issues (NEW) -- **ANR (Application Not Responding)**: Thread.sleep() calls on main thread causing app freezing -- **IllegalAccessException**: ContentProvider access failures causing crashes -- **Blocking operations**: Synchronous delays preventing app responsiveness - -## Root Cause Analysis - -The issues stemmed from: - -1. **Android 14+ Requirements**: Starting with Android 14, all foreground services must declare their type using the `android:foregroundServiceType` attribute -2. **Incomplete Notification Setup**: The service was calling `startForeground()` without proper notification channel creation -3. **Lack of Error Handling**: No fallback mechanisms when foreground service startup failed -4. **Missing Exception Handling**: Specific handling for `MissingForegroundServiceTypeException` was missing -5. **Process State Validation**: No validation that the process is in a valid state before attempting service startup -6. **Insufficient Recovery Mechanisms**: Limited fallback strategies when services fail to start -7. **ANR Issues**: Thread.sleep() calls on main thread causing application freezing -8. **ContentProvider Access Issues**: IllegalAccessException when provider calls fail - -## Solutions Implemented - -### 1. AndroidManifest.xml Fixes - -Added the required `android:foregroundServiceType` attribute to both services: - -```xml - - - -``` - -**Note**: Used `"specialUse"` as the foreground service type since this is a core system service that doesn't fit into the standard categories. - -### 2. Enhanced DaemonService Implementation - -#### Notification Channel Creation -```java -private void createNotificationChannel() { - try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - NotificationChannel channel = new NotificationChannel( - CHANNEL_ID, - CHANNEL_NAME, - NotificationManager.IMPORTANCE_LOW - ); - channel.setDescription(CHANNEL_DESCRIPTION); - channel.setShowBadge(false); - channel.setSound(null, null); - channel.enableVibration(false); - - NotificationManager notificationManager = getSystemService(NotificationManager.class); - if (notificationManager != null) { - notificationManager.createNotificationChannel(channel); - Log.d(TAG, "Notification channel created successfully"); - } - } - } catch (Exception e) { - Log.e(TAG, "Failed to create notification channel: " + e.getMessage(), e); - } -} -``` - -#### Proper Foreground Service Setup -```java -private boolean startForegroundService() { - try { - Notification notification = createNotification(); - if (notification != null) { - startForeground(NOTIFY_ID, notification); - Log.d(TAG, "Foreground service started successfully"); - return true; - } else { - Log.e(TAG, "Failed to create notification"); - return false; - } - } catch (Exception e) { - Log.e(TAG, "Failed to start foreground service: " + e.getMessage(), e); - return false; - } -} -``` - -#### Enhanced Error Handling -```java -@Override -public int onStartCommand(Intent intent, int flags, int startId) { - Log.d(TAG, "DaemonService onStartCommand"); - - try { - // Start the inner service - Intent innerIntent = new Intent(this, DaemonInnerService.class); - startService(innerIntent); - - // Start foreground service for Android 8.0+ - if (BuildCompat.isOreo()) { - if (!startForegroundService()) { - Log.w(TAG, "Failed to start foreground service, falling back to regular service"); - return START_STICKY; - } - } - - Log.d(TAG, "DaemonService started successfully"); - return START_STICKY; - - } catch (Exception e) { - Log.e(TAG, "Error starting DaemonService: " + e.getMessage(), e); - // Return START_STICKY to allow the system to restart the service - return START_STICKY; - } -} -``` - -### 3. Enhanced Service Startup Logic in BlackBoxCore - -#### Process State Validation -```java -private boolean isValidProcessState() { - try { - // Check if the context is valid - if (getContext() == null) { - Slog.w(TAG, "Context is null, process state invalid"); - return false; - } - - // Check if we're in the main process - if (!isMainProcess()) { - Slog.w(TAG, "Not in main process, skipping service start"); - return false; - } - - // Check if the process is in a good state - try { - getContext().getPackageName(); - } catch (Exception e) { - Slog.w(TAG, "Package name access failed, process state invalid: " + e.getMessage()); - return false; - } - - return true; - } catch (Exception e) { - Slog.w(TAG, "Process state validation failed: " + e.getMessage()); - return false; - } -} -``` - -#### Comprehensive Retry Mechanism with "Process is Bad" Handling -```java -boolean serviceStarted = false; -int maxRetries = 3; - -for (int retry = 0; retry < maxRetries && !serviceStarted; retry++) { - try { - if (retry > 0) { - Slog.d(TAG, "Retry attempt " + (retry + 1) + " for starting DaemonService"); - // Wait before retry - Thread.sleep(1000 * retry); - } - - if (BuildCompat.isOreo()) { - getContext().startForegroundService(intent); - Slog.d(TAG, "Started DaemonService as foreground service"); - serviceStarted = true; - } else { - getContext().startService(intent); - Slog.d(TAG, "Started DaemonService as regular service"); - serviceStarted = true; - } - - } catch (SecurityException e) { - if (e.getMessage() != null && e.getMessage().contains("MissingForegroundServiceTypeException")) { - Slog.w(TAG, "Foreground service type missing, falling back to regular service"); - try { - getContext().startService(intent); - Slog.d(TAG, "Started DaemonService as regular service (fallback)"); - serviceStarted = true; - } catch (Exception fallbackEx) { - Slog.e(TAG, "Failed to start DaemonService even as regular service: " + fallbackEx.getMessage(), fallbackEx); - handleServiceStartFailure(retry, maxRetries, e); - } - } else if (e.getMessage() != null && e.getMessage().contains("process is bad")) { - Slog.w(TAG, "Process is bad, attempting to recover and retry"); - handleProcessBadError(retry, maxRetries); - } else { - Slog.e(TAG, "Security exception starting DaemonService: " + e.getMessage(), e); - handleServiceStartFailure(retry, maxRetries, e); - } - } catch (Exception e) { - Slog.e(TAG, "Failed to start DaemonService: " + e.getMessage(), e); - handleServiceStartFailure(retry, maxRetries, e); - } -} -``` - -#### "Process is Bad" Error Recovery -```java -private void handleProcessBadError(int retry, int maxRetries) { - if (retry < maxRetries - 1) { - Slog.w(TAG, "Process is bad, attempting recovery. Attempt " + (retry + 1) + " of " + maxRetries); - - // Try to recover the process state - try { - // Wait a bit longer for process recovery - Thread.sleep(2000); - - // Try to refresh the context - refreshProcessContext(); - - } catch (Exception e) { - Slog.w(TAG, "Process recovery failed: " + e.getMessage()); - } - } else { - Slog.e(TAG, "Process recovery failed after " + maxRetries + " attempts"); - // Try alternative startup methods - tryAlternativeStartupMethods(); - } -} -``` - -#### Alternative Startup Methods -```java -private void tryAlternativeStartupMethods() { - Slog.w(TAG, "Trying alternative startup methods..."); - - try { - // Method 1: Try using a different context - Context alternativeContext = getAlternativeContext(); - if (alternativeContext != null) { - Intent intent = new Intent(); - intent.setClass(alternativeContext, DaemonService.class); - alternativeContext.startService(intent); - Slog.d(TAG, "Alternative context startup successful"); - return; - } - } catch (Exception e) { - Slog.w(TAG, "Alternative context startup failed: " + e.getMessage()); - } - - try { - // Method 2: Try using application context - Context appContext = getContext().getApplicationContext(); - if (appContext != null && appContext != getContext()) { - Intent intent = new Intent(); - intent.setClass(appContext, DaemonService.class); - appContext.startService(intent); - Slog.d(TAG, "Application context startup successful"); - return; - } - } catch (Exception e) { - Slog.w(TAG, "Application context startup failed: " + e.getMessage()); - } - - Slog.e(TAG, "All alternative startup methods failed"); -} -``` - -#### Delayed Retry Mechanism -```java -private void scheduleDelayedServiceStart() { - try { - // Schedule a delayed retry using a handler - android.os.Handler handler = new android.os.Handler(android.os.Looper.getMainLooper()); - handler.postDelayed(new Runnable() { - @Override - public void run() { - Slog.d(TAG, "Executing delayed service start"); - if (isMainProcess() && !isBlackProcessRunning()) { - startBlackProcess(); - } - } - }, 5000); // 5 second delay - - Slog.d(TAG, "Scheduled delayed service start in 5 seconds"); - } catch (Exception e) { - Slog.w(TAG, "Failed to schedule delayed service start: " + e.getMessage()); - } -} -``` - -### 4. Enhanced Server Process Service Startup - -Similar comprehensive error handling and recovery mechanisms have been implemented for the server process service startup, including: - -- Process state validation -- Retry mechanisms with exponential backoff -- "Process is bad" error recovery -- Alternative startup methods -- Delayed retry scheduling - -### 5. ANR Prevention and ContentProvider Access Fixes (NEW) - -#### Eliminating Thread.sleep() from Main Thread -```java -// OLD: Blocking operation causing ANR -Thread.sleep(1000 * retry); - -// NEW: Asynchronous scheduling -scheduleDelayedRetry(intent, retry); -``` - -#### Asynchronous Retry Mechanism -```java -private void scheduleDelayedRetry(Intent intent, int retry) { - try { - int delayMs = 1000 * retry; - Slog.d(TAG, "Scheduling delayed retry in " + delayMs + "ms"); - - android.os.Handler handler = new android.os.Handler(android.os.Looper.getMainLooper()); - handler.postDelayed(new Runnable() { - @Override - public void run() { - try { - Slog.d(TAG, "Executing delayed retry for DaemonService"); - if (BuildCompat.isOreo()) { - getContext().startForegroundService(intent); - } else { - getContext().startService(intent); - } - Slog.d(TAG, "Delayed retry successful"); - } catch (Exception e) { - Slog.e(TAG, "Delayed retry failed: " + e.getMessage()); - tryAlternativeStartupMethods(); - } - } - }, delayMs); - - } catch (Exception e) { - Slog.w(TAG, "Failed to schedule delayed retry: " + e.getMessage()); - tryAlternativeStartupMethods(); - } -} -``` - -#### Enhanced ContentProvider Access with Fallbacks -```java -private boolean isBlackProcessRunning() { - try { - // Primary method: Try to access the SystemCallProvider - try { - Bundle testBundle = new Bundle(); - testBundle.putString("_B_|_server_name_", "test"); - Bundle result = ProviderCall.callSafely(ProxyManifest.getBindProvider(), "VM", null, testBundle); - if (result != null) { - Slog.d(TAG, "Black process is running - SystemCallProvider accessible"); - return true; - } - } catch (Exception e) { - Slog.w(TAG, "Provider call failed: " + e.getMessage()); - } - - // Fallback 1: Check if the provider authority exists - try { - String authority = ProxyManifest.getBindProvider(); - if (authority != null && !authority.isEmpty()) { - android.content.pm.ProviderInfo providerInfo = getContext().getPackageManager() - .resolveContentProvider(authority, 0); - if (providerInfo != null) { - Slog.d(TAG, "Provider exists but call failed - black process may be starting"); - return false; - } - } - } catch (Exception e) { - Slog.w(TAG, "Provider resolution failed: " + e.getMessage()); - } - - // Fallback 2: Check if DaemonService is running - try { - android.app.ActivityManager am = (android.app.ActivityManager) getContext() - .getSystemService(Context.ACTIVITY_SERVICE); - if (am != null) { - for (android.app.ActivityManager.RunningServiceInfo service : am.getRunningServices(Integer.MAX_VALUE)) { - if (service.service.getClassName().contains("DaemonService")) { - Slog.d(TAG, "DaemonService is running - black process active"); - return true; - } - } - } - } catch (Exception e) { - Slog.w(TAG, "Service check failed: " + e.getMessage()); - } - - Slog.d(TAG, "Black process is not running"); - return false; - - } catch (Exception e) { - Slog.w(TAG, "Error checking black process status: " + e.getMessage()); - return false; - } -} -``` - -#### Asynchronous Provider Check -```java -private void scheduleProviderCheck() { - try { - android.os.Handler handler = new android.os.Handler(android.os.Looper.getMainLooper()); - handler.postDelayed(new Runnable() { - @Override - public void run() { - try { - Bundle testBundle = new Bundle(); - testBundle.putString("_B_|_server_name_", "test"); - Bundle result = ProviderCall.callSafely(ProxyManifest.getBindProvider(), "VM", null, testBundle); - if (result != null) { - Slog.d(TAG, "Black process started successfully, SystemCallProvider is accessible"); - } else { - Slog.w(TAG, "Black process started but SystemCallProvider is not accessible yet"); - } - } catch (Exception e) { - Slog.w(TAG, "SystemCallProvider not accessible yet, will retry later: " + e.getMessage()); - } - } - }, 1000); // 1 second delay - - } catch (Exception e) { - Slog.w(TAG, "Failed to schedule provider check: " + e.getMessage()); - } -} -``` - -## Benefits of the Fixes - -### 1. Android 14+ Compatibility -- ✅ Eliminates `MissingForegroundServiceTypeException` crashes -- ✅ Proper foreground service type declaration -- ✅ Compliance with latest Android requirements - -### 2. Improved Service Reliability -- ✅ Proper notification channel creation for Android 8.0+ -- ✅ Robust error handling and fallback mechanisms -- ✅ Better service lifecycle management - -### 3. Enhanced User Experience -- ✅ Proper notification setup with user-friendly content -- ✅ Silent notifications that don't disturb users -- ✅ Service continues running even if foreground setup fails - -### 4. Better Debugging and Maintenance -- ✅ Comprehensive error logging -- ✅ Clear fallback path documentation -- ✅ Easy identification of service startup issues - -### 5. Process State Recovery (NEW) -- ✅ **Eliminates "process is bad" errors** that prevent BlackBox from opening -- ✅ **Multiple fallback strategies** when services fail to start -- ✅ **Automatic recovery mechanisms** for process state issues -- ✅ **Delayed retry logic** to handle temporary system issues -- ✅ **Alternative context methods** for service startup - -### 6. ANR Prevention and App Responsiveness (NEW) -- ✅ **Eliminates ANR crashes** caused by Thread.sleep() on main thread -- ✅ **Non-blocking operations** ensure app remains responsive -- ✅ **Asynchronous retry mechanisms** prevent UI freezing -- ✅ **Better user experience** with smooth app operation -- ✅ **Improved app stability** during service startup - -## Testing Scenarios - -The fixes have been tested to ensure: - -1. **Android 14+ Devices**: Foreground service starts without `MissingForegroundServiceTypeException` -2. **Android 8.0-13**: Proper notification channel creation and foreground service operation -3. **Android 7.0 and below**: Regular service operation without foreground requirements -4. **Error Scenarios**: Proper fallback when foreground service fails -5. **Service Restart**: Service properly restarts after system kills -6. **Process State Issues**: Recovery from "process is bad" errors -7. **Service Startup Failures**: Multiple fallback strategies work correctly -8. **BlackBox App Opening**: App initializes successfully even with service issues -9. **ANR Prevention**: No freezing or unresponsiveness during service startup -10. **ContentProvider Access**: Graceful handling of provider access failures - -## Files Modified - -1. **`Bcore/src/main/AndroidManifest.xml`** - - Added `android:foregroundServiceType="specialUse"` to both DaemonService declarations - -2. **`Bcore/src/main/java/top/niunaijun/blackbox/core/system/DaemonService.java`** - - Enhanced notification channel creation - - Improved foreground service setup - - Better error handling and logging - - Proper service lifecycle management - -3. **`Bcore/src/main/java/top/niunaijun/blackbox/BlackBoxCore.java`** - - Enhanced service startup error handling - - Added fallback mechanisms for service failures - - Better logging and debugging information - - Proper exception handling for security issues - - **NEW: Process state validation and recovery** - - **NEW: "Process is bad" error handling** - - **NEW: Alternative startup methods** - - **NEW: Delayed retry mechanisms** - - **NEW: Comprehensive retry logic with exponential backoff** - -## Future Considerations - -1. **Android 15+**: Monitor for any new foreground service requirements -2. **Notification Content**: Consider making notification content configurable -3. **Service Priority**: Evaluate if different foreground service types would be more appropriate -4. **Battery Optimization**: Monitor impact on device battery life and optimize if needed -5. **Process Recovery**: Enhance process state recovery mechanisms based on real-world usage -6. **Startup Performance**: Optimize service startup time while maintaining reliability - -## Conclusion - -These comprehensive fixes ensure that the DaemonService operates reliably across all Android versions, particularly addressing the critical Android 14+ compatibility issues and the "process is bad" errors that were preventing BlackBox from opening. The implementation provides robust fallback mechanisms, proper error handling, and maintains the service's core functionality while ensuring compliance with modern Android requirements. - -The enhanced error recovery and process state validation significantly improve the reliability of BlackBox initialization, ensuring that users can consistently open and use the application even when encountering system-level service startup challenges. diff --git a/DEX_FILE_CORRUPTION_FIXES.md b/DEX_FILE_CORRUPTION_FIXES.md deleted file mode 100644 index 08970f1..0000000 --- a/DEX_FILE_CORRUPTION_FIXES.md +++ /dev/null @@ -1,396 +0,0 @@ -# DEX File Corruption Fixes for Bcore - -## Overview - -This document describes the comprehensive fixes implemented to resolve the **"classes.dex: Entry not found"** and **ClassNotFoundException** crashes that were causing Facebook, Instagram, and other social media apps to crash with black screens. - -## Root Cause Analysis - -Based on the logcat analysis, the main issue was **DEX file corruption** in the virtual app environment: - -### Error Pattern -``` -FATAL EXCEPTION: main -Process: com.instagram.android, PID: 25746 -java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.instagram.android/com.instagram.android.activity.MainTabActivity}: -java.lang.ClassNotFoundException: Didn't find class "com.instagram.android.activity.MainTabActivity" - -Suppressed: java.io.IOException: Failed to open dex files from /data/app/~~oF9SJEALaZB1izTEj-hzjw==/com.instagram.android-0oahNI7yhXxyR5ZXsM2bRg==/split_config.xhdpi.apk because: -Failed to find entry 'classes.dex': Entry not found -``` - -### Key Issues Identified - -1. **DEX File Corruption**: The `classes.dex` entry is missing from APK files -2. **Split APK Problems**: Issues with `split_config.xhdpi.apk` and other split APKs -3. **ClassLoader Failures**: BaseDexClassLoader cannot load classes from corrupted DEX files -4. **APK Integrity Issues**: APK files are corrupted or incomplete in the virtual environment - -## Implemented Solutions - -### 1. Enhanced ClassLoader Proxy - -**File**: `ClassLoaderProxy.java` - -#### Key Features -- **Comprehensive Error Handling**: Catches and handles all ClassLoader exceptions -- **Fallback Class Loaders**: Multiple fallback mechanisms when primary ClassLoader fails -- **Class Caching**: Caches successfully loaded classes to avoid repeated failures -- **DEX File Validation**: Validates APK files before attempting to load classes - -#### Methods Enhanced -- `loadClass()` - Enhanced with fallback mechanisms -- `findClass()` - Enhanced with error recovery -- `forName()` - Enhanced with comprehensive error handling -- `openDexFile()` - New method to handle DEX file corruption -- `loadDexFile()` - New method to handle DexPathList failures - -#### Implementation Details -```java -// Hook loadClass to handle missing classes gracefully -@ProxyMethod("loadClass") -public static class LoadClass extends MethodHook { - @Override - protected Object hook(Object who, Method method, Object[] args) throws Throwable { - String className = (String) args[0]; - - // Check cache first - if (sClassCache.containsKey(className)) { - return sClassCache.get(className); - } - - try { - // Try original method first - Object result = method.invoke(who, args); - if (result != null) { - sClassCache.put(className, (Class) result); - return result; - } - } catch (Exception e) { - // Try fallback class loaders - Class fallbackResult = tryFallbackClassLoaders(className); - if (fallbackResult != null) { - sClassCache.put(className, fallbackResult); - return fallbackResult; - } - } - - return null; // Prevent crash - } -} -``` - -### 2. DEX File Recovery Utility - -**File**: `DexFileRecovery.java` - -#### Recovery Strategies (in priority order) - -1. **Alternative APK Strategy** (Priority: 100) - - Finds alternative APK files in the same directory - - Validates APK integrity before use - - Highest priority for quick recovery - -2. **Host App APK Strategy** (Priority: 80) - - Uses the host app's APK as fallback - - Reliable fallback when virtual app APKs are corrupted - - Medium priority for stability - -3. **System APK Strategy** (Priority: 60) - - Searches system directories for valid APK files - - Uses `/system/app`, `/vendor/app`, etc. - - Lower priority but good for system compatibility - -4. **DEX Extraction Strategy** (Priority: 40) - - Attempts to extract `classes.dex` from corrupted APKs - - Creates standalone DEX files when possible - - Useful for partially corrupted APKs - -5. **Backup Restore Strategy** (Priority: 20) - - Looks for backup files (`.bak`, `.backup`, `.old`) - - Restores from previous working versions - - Lowest priority but good for long-term recovery - -#### Implementation Example -```java -public static RecoveryResult recoverDexFile(String corruptedFilePath) { - // Try each recovery strategy in order of priority - for (RecoveryStrategy strategy : sRecoveryStrategies) { - try { - RecoveryResult result = strategy.attemptRecovery(corruptedFilePath); - if (result.success) { - return result; - } - } catch (Exception e) { - // Continue to next strategy - } - } - - return new RecoveryResult("All recovery strategies failed"); -} -``` - -### 3. DEX Crash Prevention Utility - -**File**: `DexCrashPrevention.java` - -#### Prevention Mechanisms - -1. **Proactive Prevention** - - Monitors APK files for corruption - - Pre-validates critical APK files - - Prevents corruption before it causes crashes - -2. **DEX File Validation** - - Validates APK integrity before use - - Checks for `classes.dex` entry presence - - Validates file size and structure - -3. **ClassLoader Monitoring** - - Monitors ClassLoader operations - - Detects potential failures early - - Provides proactive intervention - -4. **APK Integrity Checks** - - Periodic validation of APK files - - Detects corruption before runtime - - Maintains file system health - -#### Split APK Handling -```java -private static PreventionResult handleProblematicSplitApk(File splitApk) { - // Try to find the base APK - String baseApkPath = findBaseApkPath(splitApk); - if (baseApkPath != null) { - // Use base APK instead of problematic split - return new PreventionResult("Split APK Handling", true, - "Using valid base APK instead of problematic split"); - } - - // Attempt recovery if no base APK available - DexFileRecovery.RecoveryResult recoveryResult = - DexFileRecovery.recoverDexFile(splitApk.getAbsolutePath()); - - return new PreventionResult("Split APK Recovery", - recoveryResult.success, recoveryResult.errorMessage); -} -``` - -## Integration Points - -### BlackBoxCore Integration -```java -static { - try { - // Install all crash prevention mechanisms - SimpleCrashFix.installSimpleFix(); - StackTraceFilter.install(); - SocialMediaAppCrashPrevention.initialize(); - DexCrashPrevention.initialize(); // NEW: DEX crash prevention - } catch (Exception e) { - Slog.w(TAG, "Failed to install crash prevention: " + e.getMessage()); - } -} -``` - -### HookManager Integration -The enhanced ClassLoader proxy is automatically registered in the HookManager: -```java -addInjector(new ClassLoaderProxy()); // Enhanced with DEX recovery -``` - -## Recovery Flow - -### 1. Detection Phase -- ClassLoader operations fail with `ClassNotFoundException` -- DEX file loading fails with `"classes.dex: Entry not found"` -- APK validation detects corruption - -### 2. Recovery Phase -- **Primary Recovery**: Try alternative APK files -- **Secondary Recovery**: Use host app APK as fallback -- **Tertiary Recovery**: Extract DEX from corrupted APK -- **Fallback Recovery**: Use system APK files - -### 3. Prevention Phase -- Cache successful class loads -- Validate APK integrity before use -- Monitor for future corruption -- Maintain recovery statistics - -## Configuration Options - -### Recovery Strategy Priority -```java -// Customize recovery strategy priorities -sRecoveryStrategies.sort((a, b) -> Integer.compare(b.getPriority(), a.getPriority())); -``` - -### Cache Management -```java -// Clear caches for debugging -ClassLoaderProxy.clearClassCache(); -DexFileRecovery.clearCache(); -DexCrashPrevention.clearCache(); -``` - -### Validation Settings -```java -// APK validation thresholds -private static final int MIN_APK_SIZE = 1000000; // 1MB minimum -private static final boolean STRICT_VALIDATION = false; // Relaxed validation for virtual apps -``` - -## Monitoring and Debugging - -### Log Tags -- `ClassLoaderProxy` - ClassLoader operations and recovery -- `DexFileRecovery` - DEX file recovery attempts -- `DexCrashPrevention` - Prevention mechanisms and validation - -### Status Commands -```java -// Get comprehensive status -String classLoaderStats = ClassLoaderProxy.getCacheStats(); -String recoveryStats = DexFileRecovery.getRecoveryStats(); -String preventionStats = DexCrashPrevention.getStatus(); - -Slog.d(TAG, "ClassLoader: " + classLoaderStats); -Slog.d(TAG, "Recovery: " + recoveryStats); -Slog.d(TAG, "Prevention: " + preventionStats); -``` - -### Debug Commands -```bash -# Check logs for DEX recovery -adb logcat | grep -E "(ClassLoaderProxy|DexFileRecovery|DexCrashPrevention)" - -# Check APK files -adb shell ls -la /data/app/*/*.apk - -# Check DEX extraction directory -adb shell ls -la /data/data/*/cache/dex_recovery/ -``` - -## Performance Impact - -### Memory Usage -- **Class Cache**: ~2-5MB additional memory -- **Recovery Cache**: ~1-2MB additional memory -- **Prevention Cache**: ~1-2MB additional memory -- **Total**: ~4-9MB additional memory usage - -### CPU Impact -- **Validation**: Negligible during normal operation -- **Recovery**: Only when corruption is detected -- **Monitoring**: Minimal background overhead - -### Startup Time -- **Initialization**: ~50-100ms additional startup time -- **Pre-validation**: ~100-200ms for APK validation -- **Total**: ~150-300ms additional startup time - -## Compatibility - -### Android Versions -- **Android 5.0+ (API 21+)**: Full support -- **Android 10+ (API 29+)**: Enhanced split APK support -- **Android 12+ (API 31+)**: Full DEX validation support - -### APK Types -- **Base APKs**: Full support with validation -- **Split APKs**: Enhanced support with corruption detection -- **Dynamic Feature APKs**: Basic support with fallback mechanisms - -### Device Manufacturers -- **All Manufacturers**: Universal DEX recovery support -- **Xiaomi/MIUI**: Enhanced with MIUI-specific optimizations -- **Samsung**: Full support with Knox compatibility - -## Testing Results - -### Before Fixes -- **Instagram**: Crashes immediately with black screen -- **Facebook**: Crashes on startup with ClassNotFoundException -- **WhatsApp**: Crashes during initialization -- **Error Rate**: 100% crash rate for social media apps - -### After Fixes -- **Instagram**: Successfully loads and displays content -- **Facebook**: Starts normally without crashes -- **WhatsApp**: Initializes properly -- **Error Rate**: 0% crash rate for social media apps - -## Troubleshooting - -### Common Issues - -1. **Recovery Still Failing** - - Check if all recovery strategies are initialized - - Verify APK file permissions - - Check logcat for specific error messages - -2. **Performance Degradation** - - Clear caches to reset state - - Check if too many fallback attempts - - Monitor memory usage - -3. **APK Validation Errors** - - Verify APK file integrity - - Check for split APK conflicts - - Ensure proper file permissions - -### Debug Steps - -1. **Check Recovery Status** - ```java - String status = DexCrashPrevention.getStatus(); - Slog.d(TAG, status); - ``` - -2. **Force Recovery Attempt** - ```java - DexFileRecovery.RecoveryResult result = - DexFileRecovery.forceRecovery(apkPath); - ``` - -3. **Validate Specific APK** - ```java - boolean isValid = DexFileRecovery.isValidApkFile(new File(apkPath)); - ``` - -## Future Enhancements - -### Planned Improvements - -1. **Machine Learning Corruption Detection** - - Predict corruption before it occurs - - Pattern recognition for common corruption types - - Automated prevention strategies - -2. **Enhanced Split APK Support** - - Better handling of complex split APK configurations - - Dynamic split APK validation - - Split APK recovery optimization - -3. **Performance Optimization** - - Lazy loading of recovery strategies - - Intelligent caching algorithms - - Background validation optimization - -4. **Advanced Recovery Methods** - - Network-based APK recovery - - Cloud-based DEX validation - - Peer-to-peer recovery mechanisms - -## Conclusion - -These comprehensive DEX file corruption fixes provide: - -- **100% Crash Prevention**: Eliminates ClassNotFoundException crashes -- **Robust Recovery**: Multiple fallback strategies for any corruption scenario -- **Proactive Prevention**: Detects and prevents corruption before it causes issues -- **Performance Optimized**: Minimal impact on app performance -- **Universal Compatibility**: Works across all Android versions and devices - -The implementation addresses the root cause of social media app crashes while maintaining system stability and performance. The multi-layered approach ensures that even if one recovery method fails, others will succeed, providing robust protection against DEX file corruption issues. diff --git a/DNS_INTERNET_ACCESS_FIX.md b/DNS_INTERNET_ACCESS_FIX.md deleted file mode 100644 index fb7e73e..0000000 --- a/DNS_INTERNET_ACCESS_FIX.md +++ /dev/null @@ -1,152 +0,0 @@ -# DNS and Internet Access Fix for BlackBox Sandbox - -## Problem Description -Apps inside the BlackBox sandbox were unable to access the internet when using custom DNS configurations. This was caused by incomplete networking hooks that didn't properly handle DNS resolution and network capabilities. - -## Root Causes Identified - -1. **Missing DNS Resolver Hooks**: The `IConnectivityManagerProxy` only hooked basic network info methods -2. **Incomplete Network Capabilities**: The `getNetworkCapabilities` hook was missing essential network properties -3. **No Private DNS Handling**: Android's private DNS feature bypassed the existing hooks -4. **VPN Service Limitations**: The `ProxyVpnService` was just a stub without actual network routing -5. **API Level Compatibility Issues**: Some methods required higher API levels than the minimum supported - -## Fixes Implemented - -### 1. Enhanced IConnectivityManagerProxy.java - -#### Added Missing Hooks: -- `getActiveNetwork`: Creates proper Network objects for network binding -- `getLinkProperties`: Provides DNS configuration with fallback servers -- `getPrivateDnsServerName`: Disables private DNS for sandboxed apps -- `isPrivateDnsActive`: Ensures private DNS is not active -- `getDnsServers`: Returns system DNS servers instead of custom ones -- `isNetworkValidated`: Ensures network validation passes - -#### Enhanced NetworkCapabilities: -- Added multiple transport types (WiFi + Cellular) -- Added essential capabilities (INTERNET, VALIDATED, TRUSTED, NOT_RESTRICTED) -- Added signal strength capabilities -- Proper error handling and logging - -#### API Compatibility Fixes: -- Fixed `IpPrefix` constructor usage (InetAddress + prefixLength instead of String) -- Used reflection for `setDnsServers` and `addRoute` methods -- Proper error handling for API level differences -- **Simplified route handling** - Focuses on DNS configuration which is most critical for internet access -- **Reflection-based NetworkInfo creation** - Handles API level differences gracefully -- **Generic type casting** - Proper handling of reflection generic types for NetworkInfo, Network, and NetworkCapabilities - -### 2. Enhanced ProxyVpnService.java - -#### Implemented Full VPN Service: -- Creates actual VPN interface with proper network routing -- Adds DNS servers (Google DNS as fallback) -- Handles both IPv4 and IPv6 addresses with API level checks -- Implements network monitoring -- Proper lifecycle management - -#### Network Configuration: -- Network addresses: 10.0.0.2/32 and fd00:1:fd00:1:fd00:1:fd00:1/128 (IPv6 for API 21+) -- Routes: 0.0.0.0/0 and ::/0 (IPv6 for API 21+) -- DNS servers: 8.8.8.8, 8.8.4.4, 2001:4860:4860::8888, 2001:4860:4860::8844 (IPv6 for API 21+) -- MTU: 1500 (for API 21+) - -#### API Compatibility: -- IPv6 features only enabled for API 21+ -- MTU setting only for API 21+ -- Proper reflection usage for higher API features - -### 3. Enhanced INetworkManagementServiceProxy.java - -#### Added Network Management Hooks: -- `setDnsConfigurationForNetwork`: Handles DNS configuration -- `setInterfaceConfig`: Manages network interfaces -- `addRoute`: Handles route addition -- `setUidNetworkPolicy`: Manages network policies - -### 4. New IDnsResolverProxy.java - -#### Dedicated DNS Resolution Service: -- `resolveDns`: Provides fallback DNS resolution -- `setPrivateDnsConfiguration`: Disables private DNS (API 28+) -- `setDnsServersForNetwork`: Manages DNS server configuration -- `isNetworkValidated`: Ensures network validation -- `setDnsQueryTimeout`: Sets reasonable DNS query timeouts (API 21+) -- `getDnsResolverStats`: Handles DNS stats (API 23+) - -#### API Level Compatibility: -- Private DNS hooks only for API 28+ -- DNS timeout hooks only for API 21+ -- DNS stats hooks only for API 23+ - -## How the Fixes Work - -### DNS Resolution Flow: -1. **App requests DNS resolution** → Intercepted by `IDnsResolverProxy` -2. **Private DNS disabled** → Prevents custom DNS interference -3. **Fallback DNS servers** → Google DNS (8.8.8.8, 8.8.4.4) as backup -4. **Network validation** → Always returns true to prevent connectivity issues - -### Network Capabilities: -1. **Enhanced NetworkCapabilities** → Provides all necessary network properties -2. **Proper transport types** → Supports both WiFi and cellular networks -3. **Essential capabilities** → INTERNET, VALIDATED, TRUSTED, NOT_RESTRICTED - -### VPN Service: -1. **Creates VPN interface** → Handles actual network routing -2. **DNS configuration** → Provides reliable DNS resolution -3. **Network monitoring** → Ensures continuous connectivity -4. **API compatibility** → Works across different Android versions - -## Benefits - -- ✅ **Internet access restored** for sandboxed apps -- ✅ **Custom DNS compatibility** - no more interference -- ✅ **Network validation** always passes -- ✅ **Fallback DNS servers** ensure reliability -- ✅ **Proper network binding** for all apps -- ✅ **Enhanced logging** for debugging -- ✅ **API level compatibility** across Android versions -- ✅ **Compilation errors resolved** with proper method usage - -## Testing - -After implementing these fixes: - -1. **Rebuild and reinstall** the BlackBox app -2. **Test internet access** in sandboxed apps -3. **Verify DNS resolution** works with custom DNS apps -4. **Check network connectivity** in various scenarios -5. **Monitor logs** for any remaining issues -6. **Test on different Android versions** to ensure compatibility - -## Files Modified - -- `Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IConnectivityManagerProxy.java` -- `Bcore/src/main/java/top/niunaijun/blackbox/proxy/ProxyVpnService.java` -- `Bcore/src/main/java/top/niunaijun/blackbox/fake/service/INetworkManagementServiceProxy.java` -- `Bcore/src/main/java/top/niunaijun/blackbox/fake/service/IDnsResolverProxy.java` (new) -- `Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/HookManager.java` - -## Compilation Issues Resolved - -1. **IpPrefix Constructor**: Fixed to use `new IpPrefix(InetAddress, int)` instead of `new IpPrefix(String)` -2. **setRoutes Method**: Replaced with reflection-based `addRoute` calls for API compatibility -3. **API Level Checks**: Added proper version checks for methods requiring higher API levels -4. **Reflection Usage**: Used reflection for methods that may not be available on all Android versions -5. **NetworkInfo Constructor**: Used reflection to create NetworkInfo objects for API compatibility -6. **Simplified Route Handling**: Removed complex route creation to focus on essential DNS configuration -7. **API Level Compatibility**: All methods now work across different Android versions (API 21+) -8. **Generic Type Casting**: Fixed reflection generic type issues for NetworkInfo, Network, and NetworkCapabilities -9. **Constructor Type Safety**: Proper casting of reflection-created objects to their target types - -## Notes - -- The fixes maintain backward compatibility with existing functionality -- All changes include proper error handling and logging -- The solution works across different Android versions (API 21+) -- DNS fallback ensures reliability even if primary DNS fails -- Network validation is always positive to prevent connectivity issues -- API level compatibility is maintained through reflection and version checks -- Compilation errors are resolved with proper method signatures and API usage diff --git a/FREEZING_BUG_FIXES.md b/FREEZING_BUG_FIXES.md deleted file mode 100644 index fdcd9f3..0000000 --- a/FREEZING_BUG_FIXES.md +++ /dev/null @@ -1,173 +0,0 @@ -# Freezing Bug Fixes for VSpace App - -## Problem Analysis - -Based on the logcat analysis, the main issue causing the game freezing was: - -1. **Repeated NullPointerException errors** in `callGcSupression` with the message: - ``` - java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object java.lang.reflect.Method.invoke(java.lang.Object, java.lang.Object[])' on a null object reference - ``` - -2. **Resource loading failures** when trying to load app icons and labels -3. **Memory management issues** causing performance degradation -4. **Lack of proper error handling** throughout the application - -## Fixes Implemented - -### 1. Enhanced Error Handling in AppsRepository (`app/src/main/java/top/niunaijun/blackboxa/data/AppsRepository.kt`) - -**Changes Made:** -- Added comprehensive try-catch blocks around all critical operations -- Implemented safe resource loading methods (`safeLoadAppLabel`, `safeLoadAppIcon`) -- Added null checks for application lists and individual app processing -- Improved error logging with detailed error messages -- Added fallback mechanisms for failed operations - -**Key Improvements:** -```kotlin -// Safe resource loading -private fun safeLoadAppLabel(applicationInfo: ApplicationInfo): String { - return try { - applicationInfo.packageName - } catch (e: Exception) { - Log.w(TAG, "Failed to load label for ${applicationInfo.packageName}: ${e.message}") - "Unknown App" - } -} - -// Comprehensive error handling -fun getVmInstallList(userId: Int, appsLiveData: MutableLiveData>) { - try { - // ... existing logic with null checks - } catch (e: Exception) { - Log.e(TAG, "Error in getVmInstallList: ${e.message}") - appsLiveData.postValue(emptyList()) - } -} -``` - -### 2. Improved AppsAdapter Error Handling (`app/src/main/java/top/niunaijun/blackboxa/view/apps/AppsAdapter.kt`) - -**Changes Made:** -- Added null checks for app icons to prevent crashes -- Implemented safe fallback mechanisms for failed icon loading -- Added comprehensive error handling in content setting - -**Key Improvements:** -```kotlin -override fun setContent(item: AppInfo, isSelected: Boolean, payload: Any?) { - try { - // Safely set the icon with null check - if (item.icon != null) { - binding.icon.setImageDrawable(item.icon) - } else { - binding.icon.setImageDrawable(null) - } - // ... rest of the logic - } catch (e: Exception) { - Log.e("AppsAdapter", "Error setting content for ${item.packageName}: ${e.message}") - // Set safe defaults - binding.icon.setImageDrawable(null) - binding.name.text = item.name ?: "Unknown App" - } -} -``` - -### 3. Enhanced App Initialization (`app/src/main/java/top/niunaijun/blackboxa/app/App.kt`) - -**Changes Made:** -- Added comprehensive error handling in `attachBaseContext` and `onCreate` -- Implemented individual try-catch blocks for each BlackBoxCore operation -- Added fallback mechanisms for critical initialization failures -- Improved error logging for debugging - -**Key Improvements:** -```kotlin -override fun attachBaseContext(base: Context?) { - try { - super.attachBaseContext(base) - - // Initialize BlackBoxCore with error handling - try { - BlackBoxCore.get().closeCodeInit() - } catch (e: Exception) { - Log.e("App", "Error in closeCodeInit: ${e.message}") - } - - // ... other initialization with error handling - } catch (e: Exception) { - Log.e("App", "Critical error in attachBaseContext: ${e.message}") - // Ensure we still set the context even if other initialization fails - if (base != null) { - mContext = base - } - } -} -``` - -### 4. Improved AppManager Error Handling (`app/src/main/java/top/niunaijun/blackboxa/app/AppManager.kt`) - -**Changes Made:** -- Added error handling for lazy initialization of critical components -- Implemented safe fallback mechanisms for failed operations -- Added comprehensive logging for debugging -- Prevented cascading failures - -### 5. Enhanced BlackBoxLoader (`app/src/main/java/top/niunaijun/blackboxa/view/main/BlackBoxLoader.kt`) - -**Changes Made:** -- Added error handling for all preference operations -- Implemented safe lifecycle callback management -- Added null checks for file operations -- Improved error recovery mechanisms - -### 6. Improved MainActivity (`app/src/main/java/top/niunaijun/blackboxa/view/main/MainActivity.kt`) - -**Changes Made:** -- Added comprehensive error handling for UI initialization -- Implemented safe ViewPager and adapter management -- Added error dialogs for critical failures -- Improved user experience during errors - -### 7. Enhanced AppsFragment (`app/src/main/java/top/niunaijun/blackboxa/view/apps/AppsFragment.kt`) - -**Changes Made:** -- Added error handling for all fragment lifecycle methods -- Implemented safe RecyclerView operations -- Added error handling for touch events and user interactions -- Improved data observation with error handling - -## Expected Results - -After implementing these fixes, the app should: - -1. **No longer freeze** due to NullPointerException errors -2. **Handle resource loading failures gracefully** without crashing -3. **Provide better user feedback** when errors occur -4. **Maintain stability** even when individual components fail -5. **Improve overall performance** by preventing memory leaks and deadlocks - -## Testing Recommendations - -1. **Test app startup** with various device configurations -2. **Test app loading** with large numbers of installed apps -3. **Test resource loading** with apps that have missing or corrupted resources -4. **Monitor logcat** for any remaining error patterns -5. **Test memory usage** to ensure no memory leaks - -## Additional Notes - -- The fixes maintain backward compatibility -- Error handling is non-intrusive and doesn't affect normal operation -- All error messages are logged for debugging purposes -- The app will continue to function even if some components fail to initialize - -## Build Instructions - -1. Clean and rebuild the project in Android Studio -2. Test on the target device -3. Monitor logcat for any remaining issues -4. Verify that the freezing issue is resolved - -The comprehensive error handling implemented should resolve the freezing issues identified in the logcat while maintaining the app's functionality and improving its overall stability. diff --git a/INFINITE_RECURSION_FIX.md b/INFINITE_RECURSION_FIX.md deleted file mode 100644 index 40d945c..0000000 --- a/INFINITE_RECURSION_FIX.md +++ /dev/null @@ -1,193 +0,0 @@ -# Infinite Recursion Fix for PackageManager Hook - -## Problem Analysis - -The app was crashing due to an **infinite recursion loop** in the `findActualApkPath` method: - -``` -at top.niunaijun.blackbox.fake.frameworks.BPackageManager.findActualApkPath(BPackageManager.java:696) -at top.niunaijun.blackbox.fake.frameworks.BPackageManager.createFallbackApplicationInfo(BPackageManager.java:667) -at top.niunaijun.blackbox.fake.frameworks.BPackageManager.getApplicationInfo(BPackageManager.java:304) -at top.niunaijun.blackbox.fake.service.IPackageManagerProxy$GetApplicationInfo.hook(IPackageManagerProxy.java:259) -``` - -**Root Cause:** The `findActualApkPath` method was calling `BlackBoxCore.getContext().getPackageManager().getApplicationInfo()`, which triggered the same hook again, creating an endless loop. - -## The Recursion Cycle - -1. **BPackageManager.getApplicationInfo()** is called -2. **IPackageManagerProxy$GetApplicationInfo.hook()** intercepts the call -3. **findActualApkPath()** is called to find real APK path -4. **findActualApkPath()** calls `PackageManager.getApplicationInfo()` -5. **IPackageManagerProxy$GetApplicationInfo.hook()** intercepts again -6. **findActualApkPath()** is called again -7. **Infinite loop continues** until stack overflow - -## Issues Identified - -1. **Hook Recursion**: The PackageManager is hooked, so calling it from within the hook causes recursion -2. **No Recursion Protection**: No mechanism to prevent infinite loops -3. **Fallback Dependency**: The fallback mechanism depended on the hooked PackageManager -4. **Stack Overflow**: The infinite recursion eventually causes stack overflow and crash - -## Fixes Implemented - -### 1. Recursion Protection Flag (`BPackageManager.java`) - -**Added a static flag to prevent recursion:** - -```java -private static volatile boolean sIsFindingApkPath = false; // Flag to prevent recursion -``` - -**Enhanced `findActualApkPath` method with recursion protection:** - -```java -private String findActualApkPath(String packageName) { - if (sIsFindingApkPath) { - Log.w(TAG, "findActualApkPath called recursively, returning null to prevent infinite loop."); - return null; - } - sIsFindingApkPath = true; - try { - // Skip PackageManager call to prevent infinite recursion - // The PackageManager is hooked and would cause infinite loops - Log.d(TAG, "Skipping PackageManager call to prevent recursion for " + packageName); - - // Try common paths - String[] commonPaths = { - "/data/app/" + packageName + "-1/base.apk", - "/data/app/" + packageName + "-2/base.apk", - "/data/app/" + packageName + "/base.apk", - "/system/app/" + packageName + ".apk", - "/system/priv-app/" + packageName + ".apk" - }; - - for (String path : commonPaths) { - if (new File(path).exists()) { - Log.d(TAG, "Found existing APK at: " + path); - return path; - } - } - - Log.w(TAG, "No existing APK found for " + packageName + ", using fallback path"); - return null; - } finally { - sIsFindingApkPath = false; // Reset flag - } -} -``` - -### 2. Removed PackageManager Dependency - -**Eliminated the problematic PackageManager call:** - -```java -// REMOVED: This was causing infinite recursion -// ApplicationInfo realInfo = BlackBoxCore.getContext().getPackageManager() -// .getApplicationInfo(packageName, 0); - -// REPLACED WITH: Direct file system checking -String[] commonPaths = { - "/data/app/" + packageName + "-1/base.apk", - "/data/app/" + packageName + "-2/base.apk", - "/data/app/" + packageName + "/base.apk", - "/system/app/" + packageName + ".apk", - "/system/priv-app/" + packageName + ".apk" -}; -``` - -### 3. Enhanced Fallback Mechanism - -**Improved the fallback to work without PackageManager:** - -```java -private ApplicationInfo createFallbackApplicationInfo(String packageName, int flags, int userId) { - Log.w(TAG, "Creating fallback ApplicationInfo for " + packageName); - ApplicationInfo info = new ApplicationInfo(); - info.packageName = packageName; - info.flags = flags; - info.uid = 0; // Placeholder - - // Use more realistic paths that are less likely to cause issues - String apkPath = findActualApkPath(packageName); - if (apkPath != null) { - info.sourceDir = apkPath; - info.publicSourceDir = apkPath; - } else { - // Use data app path instead of system app path - info.sourceDir = "/data/app/" + packageName + "-1/base.apk"; - info.publicSourceDir = "/data/app/" + packageName + "-1/base.apk"; - } - - info.dataDir = "/data/data/" + packageName; - info.nativeLibraryDir = "/data/app-lib/" + packageName; - info.metaData = new Bundle(); - info.splitNames = new String[]{}; - - // Set some basic flags to make it look more realistic - info.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP; - info.flags |= ApplicationInfo.FLAG_SUPPORTS_RTL; - - return info; -} -``` - -## Key Changes Made - -### 1. **Recursion Protection** -- Added `sIsFindingApkPath` flag to detect and prevent recursion -- Flag is set at the start and reset in finally block -- Early return if recursion is detected - -### 2. **Eliminated PackageManager Dependency** -- Removed calls to `PackageManager.getApplicationInfo()` -- Replaced with direct file system path checking -- Avoids triggering the hook again - -### 3. **File System Based Path Discovery** -- Check common APK installation paths directly -- Use `File.exists()` to verify APK presence -- Fallback to realistic paths when APK not found - -### 4. **Enhanced Logging** -- Added detailed logging for recursion detection -- Clear messages about skipping PackageManager calls -- Better debugging information - -## Expected Results - -After implementing these fixes, the app should: - -✅ **No more infinite recursion** crashes -✅ **Stable fallback mechanism** without PackageManager dependency -✅ **File system based APK discovery** for real paths -✅ **Graceful degradation** when APK files are not found -✅ **Proper error handling** without stack overflow -✅ **Detailed logging** for debugging path issues - -## Testing Recommendations - -1. **Test app startup** on various devices -2. **Monitor logcat** for recursion detection messages -3. **Test with missing APKs** to verify fallback paths -4. **Test with different APK locations** (data, system, priv-app) -5. **Verify no stack overflow** errors -6. **Test PackageManager operations** to ensure hooks work correctly - -## Additional Notes - -- The fix maintains backward compatibility -- File system checking is more reliable than PackageManager calls in this context -- Recursion protection prevents similar issues in the future -- Enhanced logging helps with debugging and monitoring -- The fallback mechanism is now completely independent of PackageManager hooks - -## Build Instructions - -1. Clean and rebuild the project in Android Studio -2. Test app startup on the target device -3. Monitor logcat for any recursion-related messages -4. Verify that the infinite loop is resolved - -The comprehensive recursion protection and PackageManager independence implemented should resolve the infinite recursion crashes and allow the app to start successfully. diff --git a/JAR_IMPROVEMENTS.md b/JAR_IMPROVEMENTS.md deleted file mode 100644 index 85313a5..0000000 --- a/JAR_IMPROVEMENTS.md +++ /dev/null @@ -1,234 +0,0 @@ -# JAR File System Improvements - -## Overview -This document outlines the comprehensive improvements made to the JAR file handling system in the BlackBox framework. The original implementation was basic and prone to errors, while the new system provides robust, efficient, and maintainable JAR file management. - -## Key Improvements - -### 1. **New JarManager Class** -- **Location**: `Bcore/src/main/java/top/niunaijun/blackbox/core/system/JarManager.java` -- **Purpose**: Centralized JAR file management with advanced features -- **Key Features**: - - Singleton pattern for global access - - Async and sync initialization options - - File integrity verification - - Caching with hash-based validation - - Comprehensive error handling - - Progress tracking during file operations - -### 2. **JarConfig Configuration Class** -- **Location**: `Bcore/src/main/java/top/niunaijun/blackbox/core/system/JarConfig.java` -- **Purpose**: Centralized configuration management -- **Key Features**: - - JAR file definitions with metadata - - Configurable validation settings - - Performance optimization settings - - Memory-aware buffer sizing - - Retry and timeout configurations - -### 3. **JarUtils Utility Class** -- **Location**: `Bcore/src/main/java/top/niunaijun/blackbox/utils/JarUtils.java` -- **Purpose**: Enhanced JAR file operations -- **Key Features**: - - Atomic file operations with temporary files - - Progress tracking during copy operations - - SHA-256 hash calculation - - JAR file integrity verification - - Safe file deletion with retry logic - - Memory-optimized buffer sizing - -### 4. **Enhanced NativeCore Integration** -- **Location**: `Bcore/src/main/java/top/niunaijun/blackbox/core/NativeCore.java` -- **Improvements**: - - Better error handling in `loadEmptyDex()` - - Automatic fallback to sync initialization - - Detailed logging for debugging - - Graceful handling of missing JAR files - -## Technical Improvements - -### **Error Handling** -- **Before**: Basic try-catch with `e.printStackTrace()` -- **After**: Comprehensive error handling with: - - Detailed error messages - - Graceful fallbacks - - Proper resource cleanup - - Retry mechanisms with exponential backoff - -### **Performance Optimization** -- **Before**: Fixed 4KB buffer size -- **After**: Memory-aware buffer sizing: - - 8KB for devices with < 256MB RAM - - 16KB for devices with 256MB-512MB RAM - - 32KB for devices with > 512MB RAM - -### **File Integrity** -- **Before**: No integrity checking -- **After**: Multiple validation layers: - - File size validation - - SHA-256 hash verification - - JAR file structure validation - - Atomic write operations - -### **Caching System** -- **Before**: No caching -- **After**: Intelligent caching with: - - File reference caching - - Hash-based validation - - Definition metadata storage - - Cache statistics and monitoring - -### **Async Operations** -- **Before**: Synchronous file operations -- **After**: Async initialization with: - - Non-blocking startup - - Progress tracking - - Fallback to sync if needed - - Thread safety with atomic operations - -## Configuration Options - -### **JarConfig Settings** -```java -// Performance settings -public static final int DEFAULT_BUFFER_SIZE = 8192; // 8KB -public static final int MAX_BUFFER_SIZE = 32768; // 32KB -public static final int MIN_BUFFER_SIZE = 1024; // 1KB - -// Retry settings -public static final int MAX_RETRY_ATTEMPTS = 3; -public static final long RETRY_DELAY_MS = 1000; // 1 second -public static final long MAX_RETRY_DELAY_MS = 5000; // 5 seconds - -// Validation settings -public static final boolean ENABLE_FILE_HASHING = true; -public static final boolean ENABLE_SIZE_VALIDATION = true; -public static final boolean ENABLE_ASYNC_LOADING = true; -``` - -### **JAR File Definitions** -```java -// Empty JAR for fallback DEX loading -public static final JarDefinition EMPTY_JAR = new JarDefinition( - "empty.jar", "empty.apk", 100L, - "Empty JAR for fallback DEX loading", true -); - -// JUnit JAR for testing framework support -public static final JarDefinition JUNIT_JAR = new JarDefinition( - "junit.jar", "junit.apk", 1000L, - "JUnit testing framework support", false -); -``` - -## Usage Examples - -### **Basic Usage** -```java -// Get JarManager instance -JarManager jarManager = JarManager.getInstance(); - -// Initialize asynchronously -jarManager.initializeAsync(); - -// Get JAR files -File emptyJar = jarManager.getEmptyJar(); -File junitJar = jarManager.getJunitJar(); - -// Check if ready -if (jarManager.isReady()) { - // Use JAR files -} -``` - -### **Advanced Usage** -```java -// Get detailed information -String stats = jarManager.getCacheStats(); -String jarInfo = jarManager.getJarInfo("empty.jar"); - -// Clear cache if needed -jarManager.clearCache(); - -// Force sync initialization -jarManager.initializeSync(); -``` - -### **Testing** -```java -// Run comprehensive tests -JarManagerTest.testJarManager(); -JarManagerTest.testConfiguration(); -``` - -## Migration Guide - -### **From Old System** -1. **Remove old code**: The old `initJarEnv()` method in `BlackBoxSystem` has been replaced -2. **Update imports**: Use `JarManager` instead of direct file operations -3. **Update NativeCore**: `loadEmptyDex()` now uses `JarManager.getEmptyJar()` - -### **Backward Compatibility** -- All existing functionality is preserved -- JAR files are still copied to the same locations -- File paths remain unchanged -- API compatibility maintained - -## Benefits - -### **Reliability** -- Robust error handling prevents crashes -- File integrity verification ensures data consistency -- Atomic operations prevent corruption -- Retry mechanisms handle transient failures - -### **Performance** -- Memory-optimized buffer sizes -- Async initialization reduces startup time -- Intelligent caching reduces I/O operations -- Progress tracking for large files - -### **Maintainability** -- Centralized configuration management -- Comprehensive logging for debugging -- Modular design with clear separation of concerns -- Extensive documentation and examples - -### **Scalability** -- Configurable settings for different device capabilities -- Memory-aware optimizations -- Efficient resource management -- Extensible architecture for future JAR types - -## Testing - -### **Automated Tests** -- `JarManagerTest` provides comprehensive testing -- Configuration validation -- File operation verification -- Error handling validation - -### **Manual Testing** -- Verify JAR files are copied correctly -- Check file integrity and hashes -- Monitor performance and memory usage -- Test error scenarios and recovery - -## Future Enhancements - -### **Planned Features** -- Support for additional JAR file types -- Dynamic JAR loading from network -- Compression and optimization -- Advanced caching strategies -- Performance monitoring and metrics - -### **Extensibility** -- Plugin architecture for custom JAR handlers -- Configuration-driven JAR definitions -- Custom validation rules -- Integration with external systems - -## Conclusion - -The improved JAR file system provides a solid foundation for reliable, efficient, and maintainable JAR file management in the BlackBox framework. The modular design, comprehensive error handling, and performance optimizations ensure the system can handle the demands of modern Android applications while maintaining backward compatibility. diff --git a/NULLPOINTER_EXCEPTION_FIX.md b/NULLPOINTER_EXCEPTION_FIX.md deleted file mode 100644 index d346e3b..0000000 --- a/NULLPOINTER_EXCEPTION_FIX.md +++ /dev/null @@ -1,221 +0,0 @@ -# NullPointerException Fix for IBPackageManagerService - -## Problem Analysis - -The app was crashing with a `NullPointerException` during application startup: - -``` -java.lang.NullPointerException: Attempt to invoke interface method 'android.content.pm.ApplicationInfo top.niunaijun.blackbox.core.system.pm.IBPackageManagerService.getApplicationInfo(java.lang.String, int, int)' on a null object reference -``` - -**Root Cause:** The `IBPackageManagerService` was null when the app tried to get application information during the binding process. - -## Issues Identified - -1. **Service Initialization Failure**: The PackageManager service wasn't being properly initialized -2. **No Null Checks**: Methods were calling `getService().method()` without checking if the service was null -3. **No Fallback Mechanisms**: When the service failed, there were no fallback options -4. **Service Health Issues**: The service could become null after initialization due to binder death - -## Fixes Implemented - -### 1. Enhanced Service Management in BlackManager (`Bcore/src/main/java/top/niunaijun/blackbox/fake/frameworks/BlackManager.java`) - -**Improvements:** -- Added service health checks -- Implemented retry mechanisms with timeouts -- Added service death handling -- Enhanced error logging - -**Key Features:** -```java -// Service health check -public boolean isServiceHealthy() { - if (mService == null) { - return false; - } - try { - return mService.asBinder().pingBinder() && mService.asBinder().isBinderAlive(); - } catch (Exception e) { - Log.w(TAG, "Service health check failed for " + getServiceName(), e); - return false; - } -} - -// Service cache clearing -public void clearServiceCache() { - mService = null; - Log.d(TAG, "Cleared service cache for " + getServiceName()); -} -``` - -### 2. Comprehensive Null Checks in BPackageManager (`Bcore/src/main/java/top/niunaijun/blackbox/fake/frameworks/BPackageManager.java`) - -**Changes Made:** -- Added null checks before calling service methods -- Implemented fallback mechanisms for all critical methods -- Enhanced error handling with specific exception types -- Added service reinitialization capabilities - -**Key Improvements:** -```java -public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) { - try { - IBPackageManagerService service = getServiceWithFallback(); - if (service == null) { - Log.w(TAG, "PackageManager service is null for getApplicationInfo, using fallback"); - return createFallbackApplicationInfo(packageName, flags, userId); - } - return service.getApplicationInfo(packageName, flags, userId); - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in getApplicationInfo for " + packageName, e); - return createFallbackApplicationInfo(packageName, flags, userId); - } catch (Exception e) { - Log.e(TAG, "Exception in getApplicationInfo for " + packageName, e); - return createFallbackApplicationInfo(packageName, flags, userId); - } -} -``` - -### 3. Fallback Data Creation - -**Implemented fallback methods for critical data structures:** - -```java -private ApplicationInfo createFallbackApplicationInfo(String packageName, int flags, int userId) { - Log.w(TAG, "Creating fallback ApplicationInfo for " + packageName); - ApplicationInfo info = new ApplicationInfo(); - info.packageName = packageName; - info.flags = flags; - info.uid = 0; // Placeholder - info.sourceDir = "/system/app/" + packageName + ".apk"; // Placeholder - info.dataDir = "/data/data/" + packageName; // Placeholder - info.nativeLibraryDir = "/data/app-lib/" + packageName; // Placeholder - info.publicSourceDir = "/system/app/" + packageName + ".apk"; // Placeholder - info.publicDataDir = "/data/data/" + packageName; // Placeholder - info.publicNativeLibraryDir = "/data/app-lib/" + packageName; // Placeholder - info.metaData = new Bundle(); // Placeholder - info.splitNames = new String[]{}; // Placeholder - info.permission = new String[]{}; // Placeholder - info.sharedUserId = ""; // Placeholder - info.sharedUserLabel = ""; // Placeholder - return info; -} - -private PackageInfo createFallbackPackageInfo(String packageName, int flags, int userId) { - Log.w(TAG, "Creating fallback PackageInfo for " + packageName); - PackageInfo info = new PackageInfo(); - info.packageName = packageName; - info.versionCode = 1; // Placeholder - info.versionName = "1.0"; // Placeholder - info.applicationInfo = createFallbackApplicationInfo(packageName, flags, userId); - info.firstInstallTime = System.currentTimeMillis(); // Placeholder - info.lastUpdateTime = System.currentTimeMillis(); // Placeholder - info.installLocation = 0; // Placeholder - info.size = 0; // Placeholder - info.gids = new int[]{}; // Placeholder - info.seinfo = new String[]{}; // Placeholder - info.splitNames = new String[]{}; // Placeholder - info.permission = new String[]{}; // Placeholder - info.sharedUserId = ""; // Placeholder - info.sharedUserLabel = ""; // Placeholder - info.signatures = new Signature[]{}; // Placeholder - return info; -} -``` - -### 4. Enhanced Service Reinitialization - -**Added methods to handle service failures:** - -```java -public void forceReinitialize() { - Log.d(TAG, "Force reinitializing PackageManager service"); - clearServiceCache(); - resetTransactionThrottler(); - - // Try to get the service again - try { - IBPackageManagerService service = getService(); - if (service != null) { - Log.d(TAG, "Successfully reinitialized PackageManager service"); - } else { - Log.w(TAG, "Failed to reinitialize PackageManager service"); - } - } catch (Exception e) { - Log.e(TAG, "Error during service reinitialization", e); - } -} - -public IBPackageManagerService getServiceWithFallback() { - IBPackageManagerService service = getService(); - if (service == null) { - Log.w(TAG, "PackageManager service is null, attempting reinitialization"); - forceReinitialize(); - service = getService(); - } - return service; -} -``` - -### 5. Enhanced BlackBoxCore Service Initialization (`Bcore/src/main/java/top/niunaijun/blackbox/BlackBoxCore.java`) - -**Added special handling for PackageManager service:** - -```java -// Special handling for PackageManager service -try { - IBinder packageManagerBinder = getServiceInternal(ServiceManager.PACKAGE_MANAGER); - if (packageManagerBinder == null) { - Slog.w(TAG, "PackageManager service binder is null, attempting fallback initialization"); - // Try to initialize with a delay - mHandler.postDelayed(() -> { - try { - getServiceInternal(ServiceManager.PACKAGE_MANAGER); - Slog.d(TAG, "PackageManager service initialized on retry"); - } catch (Exception e) { - Slog.e(TAG, "Failed to initialize PackageManager service on retry", e); - } - }, 1000); // 1 second delay - } -} catch (Exception e) { - Slog.e(TAG, "Error initializing PackageManager service", e); -} -``` - -## Expected Results - -After implementing these fixes, the app should: - -1. **Handle null services gracefully** without crashing -2. **Provide fallback data** when services are unavailable -3. **Automatically reinitialize** failed services -4. **Continue functioning** even with limited service availability -5. **Log detailed information** for debugging service issues -6. **Recover from service failures** automatically - -## Testing Recommendations - -1. **Test app startup** on various devices -2. **Test with limited permissions** to simulate service failures -3. **Monitor logcat** for service-related messages -4. **Test service recovery** after binder death -5. **Verify fallback data** is used when services fail -6. **Test on tablets** with different security policies - -## Additional Notes - -- The fixes maintain backward compatibility -- Fallback data provides basic functionality when services fail -- Enhanced logging helps with debugging service issues -- Service reinitialization prevents permanent failures -- The app will continue to work even with limited service availability - -## Build Instructions - -1. Clean and rebuild the project in Android Studio -2. Test app startup on the target device -3. Monitor logcat for any remaining service issues -4. Verify that the NullPointerException is resolved - -The comprehensive null checking and fallback mechanisms implemented should resolve the NullPointerException crashes while maintaining the app's functionality across different devices and scenarios. diff --git a/REAL_WORLD_APK_PATH_FIX.md b/REAL_WORLD_APK_PATH_FIX.md deleted file mode 100644 index 92edbcf..0000000 --- a/REAL_WORLD_APK_PATH_FIX.md +++ /dev/null @@ -1,258 +0,0 @@ -# Real-World APK Path Fix - -## Problem Analysis - -The previous fallback paths were using simplified formats that don't match the actual APK installation structure on modern Android devices. The real APK paths use a complex hash-based directory structure: - -**Example Real Path:** -``` -/data/app/~~LqmY0IHlKQJtJ6Cih1C6jA==/com.linkedin.android-G8hAJLQTgTz1gJ0rAUxcqA==/base.apk -``` - -**Previous Incorrect Paths:** -``` -/data/app/com.twitter.android-1/base.apk ❌ (Doesn't exist) -``` - -## Issues Identified - -1. **Incorrect Path Format**: Using simplified paths that don't match real Android APK installation structure -2. **Missing Hash Directories**: Not accounting for the `~~hash==` directory structure -3. **Missing Package Hashes**: Not accounting for package-specific hash suffixes -4. **Limited Path Discovery**: Only checking a few basic paths -5. **No Dynamic Discovery**: Not searching through actual directory structures - -## Real-World APK Path Structure - -### Modern Android APK Installation Format -``` -/data/app/~~{INSTALL_HASH}==/{PACKAGE_NAME}-{PACKAGE_HASH}==/base.apk -``` - -**Components:** -- `~~{INSTALL_HASH}==` - Installation session hash (e.g., `~~LqmY0IHlKQJtJ6Cih1C6jA==`) -- `{PACKAGE_NAME}-{PACKAGE_HASH}==` - Package name with unique hash (e.g., `com.linkedin.android-G8hAJLQTgTz1gJ0rAUxcqA==`) -- `base.apk` - The actual APK file - -### Legacy Paths (Still Used) -``` -/data/app/{PACKAGE_NAME}-{VERSION}/base.apk -/data/app/{PACKAGE_NAME}/base.apk -``` - -### System Paths -``` -/system/app/{PACKAGE_NAME}.apk -/system/priv-app/{PACKAGE_NAME}.apk -/system_ext/app/{PACKAGE_NAME}.apk -/product/app/{PACKAGE_NAME}.apk -/vendor/app/{PACKAGE_NAME}.apk -``` - -## Fixes Implemented - -### 1. Enhanced Path Discovery (`BPackageManager.java`) - -**Updated `findActualApkPath` method with real-world paths:** - -```java -private String findActualApkPath(String packageName) { - if (sIsFindingApkPath) { - Log.w(TAG, "findActualApkPath called recursively, returning null to prevent infinite loop."); - return null; - } - sIsFindingApkPath = true; - try { - // Skip PackageManager call to prevent infinite recursion - Log.d(TAG, "Skipping PackageManager call to prevent recursion for " + packageName); - - // Try common paths including real-world hash-based paths - String[] commonPaths = { - // Real-world hash-based paths (most common) - "/data/app/~~*/" + packageName + "-*/base.apk", - "/data/app/~~*/" + packageName + "*/base.apk", - - // Legacy paths - "/data/app/" + packageName + "-1/base.apk", - "/data/app/" + packageName + "-2/base.apk", - "/data/app/" + packageName + "/base.apk", - - // System paths - "/system/app/" + packageName + ".apk", - "/system/priv-app/" + packageName + ".apk", - "/system_ext/app/" + packageName + ".apk", - "/product/app/" + packageName + ".apk", - "/vendor/app/" + packageName + ".apk" - }; - - // First try exact path matching - for (String path : commonPaths) { - if (isValidApkPath(path)) { - Log.d(TAG, "Found existing APK at: " + path); - return path; - } - } - - // If exact paths don't work, try to find hash-based paths dynamically - String hashBasedPath = findHashBasedApkPath(packageName); - if (hashBasedPath != null) { - Log.d(TAG, "Found hash-based APK at: " + hashBasedPath); - return hashBasedPath; - } - - Log.w(TAG, "No existing APK found for " + packageName + ", using null path"); - return null; - } finally { - sIsFindingApkPath = false; // Reset flag - } -} -``` - -### 2. Dynamic Hash-Based Path Discovery (`BPackageManager.java`) - -**Added `findHashBasedApkPath` method for real-world path discovery:** - -```java -private String findHashBasedApkPath(String packageName) { - try { - File dataAppDir = new File("/data/app"); - if (!dataAppDir.exists() || !dataAppDir.isDirectory()) { - return null; - } - - // Look for hash directories (~~hash==) - File[] hashDirs = dataAppDir.listFiles((dir, name) -> name.startsWith("~~") && name.endsWith("==")); - if (hashDirs == null) { - return null; - } - - for (File hashDir : hashDirs) { - if (!hashDir.isDirectory()) { - continue; - } - - // Look for package directories within hash directories - File[] packageDirs = hashDir.listFiles((dir, name) -> name.startsWith(packageName)); - if (packageDirs == null) { - continue; - } - - for (File packageDir : packageDirs) { - if (!packageDir.isDirectory()) { - continue; - } - - // Look for base.apk within package directory - File baseApk = new File(packageDir, "base.apk"); - if (isValidApkPath(baseApk.getAbsolutePath())) { - return baseApk.getAbsolutePath(); - } - } - } - } catch (Exception e) { - Log.d(TAG, "Error searching for hash-based APK path for " + packageName + ": " + e.getMessage()); - } - - return null; -} -``` - -### 3. Enhanced Path Validation (`BPackageManager.java`) - -**Updated `isValidApkPath` to handle wildcard patterns:** - -```java -private boolean isValidApkPath(String path) { - try { - // Skip wildcard patterns - they need to be resolved first - if (path.contains("*")) { - return false; - } - - File apkFile = new File(path); - if (!apkFile.exists()) { - return false; - } - - // Additional validation: check if it's readable and has reasonable size - if (!apkFile.canRead()) { - Log.d(TAG, "APK file not readable: " + path); - return false; - } - - long fileSize = apkFile.length(); - if (fileSize < 1024) { // Less than 1KB is probably not a valid APK - Log.d(TAG, "APK file too small: " + path + " (size: " + fileSize + ")"); - return false; - } - - return true; - } catch (Exception e) { - Log.d(TAG, "Error checking APK path " + path + ": " + e.getMessage()); - return false; - } -} -``` - -## Key Changes Made - -### 1. **Real-World Path Patterns** -- Added hash-based directory patterns (`~~hash==`) -- Added package hash patterns (`package-hash==`) -- Included all system partition paths -- Added legacy path support - -### 2. **Dynamic Path Discovery** -- Scan `/data/app` directory for hash directories -- Search within hash directories for package directories -- Find `base.apk` files within package directories -- Handle multiple installation sessions - -### 3. **Enhanced Validation** -- Skip wildcard patterns that need resolution -- Validate file existence, readability, and size -- Comprehensive error handling -- Detailed logging for debugging - -### 4. **Comprehensive Path Coverage** -- Modern hash-based paths (most common) -- Legacy version-based paths -- All system partition paths -- Dynamic discovery for unknown patterns - -## Expected Results - -After implementing these fixes, the app should: - -✅ **Find real APK paths** using hash-based directory structure -✅ **Handle modern Android installations** correctly -✅ **Support legacy installations** for older devices -✅ **Discover APKs dynamically** when exact paths are unknown -✅ **Validate APK files** before using them -✅ **Provide detailed logging** for path discovery - -## Testing Recommendations - -1. **Test on modern Android devices** (Android 10+) -2. **Test on legacy devices** (Android 9 and below) -3. **Monitor logcat** for path discovery messages -4. **Test with different APK installation methods** -5. **Verify hash-based path discovery** works correctly -6. **Test with system apps** in different partitions - -## Additional Notes - -- The fix supports both modern and legacy APK installation formats -- Dynamic discovery handles unknown hash patterns -- Enhanced validation prevents using invalid APK files -- Comprehensive logging helps with debugging path issues -- The solution is backward compatible with older Android versions - -## Build Instructions - -1. Clean and rebuild the project in Android Studio -2. Test app startup on the target device -3. Monitor logcat for path discovery messages -4. Verify that real APK paths are found correctly - -The comprehensive real-world APK path discovery implemented should resolve the path issues and allow the app to find actual APK files on modern Android devices. diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 76326ea..96fd400 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,370 +1,173 @@ -# BlackBox Virtual Environment - Release Notes +# Release Notes - NewBlackbox -## Version 2.4.0 - "Comprehensive Virtualization" 🚀 +## Version: Latest Build (2026-01-30) -**Release Date**: August 2024 -**Android Support**: 8.0+ (API 26+) -**Minimum Requirements**: 2GB RAM, 2GB Storage +### Major Fixes + +#### 1. Fixed Instagram and App Crashes on Android 14+ + +**Problem:** Apps like Instagram were crashing with `SecurityException` due to package/UID verification failures in `SensitiveContentProtectionManagerService`. + +**Solution:** +- Created `ISensitiveContentProtectionManagerProxy` to hook the Android 14+ sensitive content protection service +- Intercepts `setSensitiveContentProtection()` calls and replaces guest package names with host package name +- Properly implements `getWho()` using reflection to obtain the original service interface +- Includes safety checks to only hook when service exists (Android 14+) + +**Files Modified:** +- `Bcore/src/main/java/top/niunaijun/blackbox/fake/service/ISensitiveContentProtectionManagerProxy.java` (NEW) +- `Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/HookManager.java` + +**Impact:** Instagram and other apps now work correctly on Android 14+ devices --- -## 🎯 Major New Features +#### 2. Fixed ContentProvider Call Method Crashes -### ✨ Complete WebView & Browser Support -- **WebView Proxy System**: Comprehensive WebView compatibility layer -- **Data Directory Isolation**: Each virtual app gets unique WebView storage -- **Process Conflict Prevention**: Eliminates WebView conflicts between apps -- **Browser App Compatibility**: Full support for Chrome, Firefox, and other browsers -- **PWA Support**: Progressive Web Apps work seamlessly -- **WebView Factory Proxy**: Handles WebView provider selection and initialization +**Problem:** Apps were crashing when ContentProviders' `call()` method had `AttributionSource` objects nested deep within `Bundle` arguments. -### 🔐 Enhanced Google Services Integration -- **Google Account Manager Proxy**: Comprehensive account management -- **Mock Google Accounts**: Automatic mock account creation for virtual environment -- **Authentication Token Handling**: Manages Google auth tokens and refresh -- **GMS Compatibility**: Enhanced Google Play Services integration -- **Account Synchronization**: Handles account sync operations gracefully +**Solution:** +- Created `AttributionSourceUtils` utility class with recursive Bundle traversal +- Handles deeply nested `AttributionSource` objects in Bundles and Parcelables +- Correctly fixes UID to use `getHostUid()` instead of `getBUid()` -### ⚙️ Advanced Background Job Management -- **WorkManager Compatibility**: Complete WorkManager support -- **JobScheduler UID Bypass**: Smart UID validation bypass -- **Background Task Recovery**: Graceful handling of failed background jobs -- **UID Spoofing Helper**: Intelligent UID selection for system operations -- **Job Error Prevention**: Prevents crashes from job scheduling failures +**Files Modified:** +- `Bcore/src/main/java/top/niunaijun/blackbox/utils/AttributionSourceUtils.java` (NEW) +- `Bcore/src/main/java/top/niunaijun/blackbox/fake/service/context/providers/ContentProviderStub.java` +- `Bcore/src/main/java/top/niunaijun/blackbox/fake/service/context/providers/SystemProviderStub.java` -### 🛡️ Comprehensive Crash Prevention -- **Context Safety Wrapper**: Prevents null context crashes -- **Activity Lifecycle Protection**: Enhanced activity creation and management -- **Service Initialization Fallbacks**: Robust service creation with fallbacks -- **Resource Loading Safety**: Safe resource and asset loading -- **Global Exception Handler**: Catches and handles unexpected errors +**Impact:** Apps using ContentProvider for deep links, inter-app communication, and settings no longer crash --- -## 🔧 Core Improvements +#### 3. Fixed SettingsProvider "Invalid Method" Errors -### App Installation & Management -- **Cloning Prevention**: Prevents installing BlackBox from within BlackBox -- **Package Validation**: Enhanced APK validation and integrity checks -- **Installation Error Handling**: Better error messages and recovery -- **Multi-User Support**: Improved virtual user management -- **App Data Isolation**: Better separation between virtual apps +**Problem:** System SettingsProvider was receiving the package name instead of the actual method name (e.g., "GET_global"), causing "Invalid method" errors and crashes. -### Virtual Environment Stability -- **Service Availability Management**: Smart service initialization and fallbacks -- **Process Communication**: Enhanced inter-process communication -- **Timeout Handling**: Prevents hanging during initialization -- **Resource Management**: Better memory and CPU usage -- **Error Recovery**: Automatic recovery from common failures +**Solution:** +- Updated `SystemProviderStub` and `ContentProviderStub` to NOT replace String arguments for `call()` methods +- String arguments in `call()` are method names like "GET_global", "PUT_system", etc., not package names +- Only replaces package names for query/insert/update/delete operations where appropriate -### System Integration -- **UID Management**: Smart UID spoofing for system compatibility -- **Permission Handling**: Enhanced permission management -- **Content Provider Safety**: Safe ContentProvider operations -- **Settings Access**: Bypasses UID mismatch issues with system settings -- **Feature Flag Support**: Handles Android feature flag queries +**Files Modified:** +- `Bcore/src/main/java/top/niunaijun/blackbox/fake/service/context/providers/SystemProviderStub.java` +- `Bcore/src/main/java/top/niunaijun/blackbox/fake/service/context/providers/ContentProviderStub.java` + +**Impact:** Settings-related operations and system preferences now work correctly --- -## 🆕 New Components +#### 4. Improved ContentProvider Error Handling -### Service Proxies -- **WebViewProxy**: Handles WebView data directory conflicts -- **WebViewFactoryProxy**: Manages WebView factory initialization -- **WorkManagerProxy**: Provides WorkManager compatibility -- **GoogleAccountManagerProxy**: Manages Google account operations -- **IMiuiSecurityManagerProxy**: Bypasses MIUI/HyperOS security checks -- **ISettingsProviderProxy**: Handles Settings ContentProvider access -- **FeatureFlagUtilsProxy**: Manages Android feature flags -- **MediaRecorderClassProxy**: Handles media recording UID issues +**Problem:** ContentProvider `call()` methods were returning `null` on errors, causing `NullPointerException` crashes in apps expecting valid Bundle responses. -### Utility Classes -- **UIDSpoofingHelper**: Smart UID selection and management -- **SimpleCrashFix**: Global crash prevention and recovery -- **XiaomiPermissionManager**: Handles Xiaomi-specific permissions +**Solution:** +- Updated `getSafeDefaultValue()` in `ContentProviderStub` to return empty `Bundle()` instead of `null` for `call()` method +- Prevents NPE crashes in apps that don't null-check ContentProvider responses -### Enhanced Hooks -- **HCallbackProxy**: Improved activity launch handling -- **AppInstrumentation**: Better activity creation and lifecycle -- **BActivityThread**: Enhanced application binding and service creation -- **ContentProviderStub**: Safe ContentProvider operations +**Files Modified:** +- `Bcore/src/main/java/top/niunaijun/blackbox/fake/service/context/providers/ContentProviderStub.java` + +**Impact:** Apps handle ContentProvider errors gracefully without crashes --- -## 🐛 Bug Fixes +### Technical Improvements -### Critical Issues Resolved -- **Black Screen Crashes**: Fixed null context and resource loading issues -- **App Launch Failures**: Resolved activity creation and binding problems -- **WebView Crashes**: Eliminated WebView data directory conflicts -- **Background Job Failures**: Fixed UID validation and job scheduling issues -- **Google Services Crashes**: Resolved GMS and account manager failures +#### AttributionSource Handling +- Implemented comprehensive recursive traversal of Bundle and Parcelable structures +- Handles nested AttributionSource objects at any depth +- Properly clones Bundles and Parcelables to avoid modifying shared state +- Includes safety checks for null values and class loading errors -### Stability Improvements -- **Service Initialization**: Fixed hanging during service startup -- **Process Communication**: Resolved inter-process communication failures -- **Resource Management**: Better memory allocation and cleanup -- **Error Handling**: Comprehensive error catching and recovery -- **Timeout Issues**: Fixed hanging operations with proper timeouts - -### Compatibility Issues -- **Android Version Support**: Better compatibility across Android versions -- **Device-Specific Issues**: Resolved Xiaomi/HyperOS specific problems -- **Permission Denials**: Bypassed common permission denial issues -- **UID Mismatches**: Fixed UID validation failures -- **Content Provider Errors**: Resolved ContentProvider access issues +#### Hook Architecture +- Improved `BinderInvocationStub` usage patterns +- Added proper `getWho()` implementations using reflection +- Enhanced `isBadEnv()` checks to detect and recover from hook failures +- Added service existence checks before attempting to hook Android version-specific services --- -## 🔄 Performance Enhancements +### Known Limitations -### Speed Improvements -- **Faster App Launch**: Optimized activity creation and binding -- **Quick Service Start**: Improved service initialization speed -- **Efficient Resource Loading**: Better resource management -- **Optimized WebView**: Faster WebView initialization and loading -- **Reduced Memory Usage**: Better memory allocation and cleanup +#### Browser Compatibility +**GeckoView/Chromium-based browsers (e.g., Nira Browser):** +- These browsers use native rendering engines (libxul.so for Gecko, Chromium for Chrome-based) +- Native crashes occur during engine initialization due to: + - Multi-process architecture conflicts + - Seccomp syscall filtering incompatibilities + - Low-level shared memory/IPC issues + +**Recommendation:** Use WebView-based browsers instead: +- Via Browser +- Privacy Browser +- JQuarks Browser +- Simple Browser -### Resource Optimization -- **Memory Management**: Improved memory allocation strategies -- **CPU Usage**: Reduced unnecessary CPU operations -- **Storage Efficiency**: Better storage management -- **Network Optimization**: Improved network request handling -- **Battery Life**: Reduced background resource consumption +These use Android's built-in WebView and work correctly in virtualized environments. --- -## 🛡️ Security Enhancements +### Testing -### Anti-Detection Features -- **UID Spoofing**: Intelligent UID manipulation for system compatibility -- **Process Hiding**: Better process isolation and hiding -- **System Property Spoofing**: Enhanced system property manipulation -- **File System Hooks**: Advanced file system operation interception -- **Debug Detection Bypass**: Prevents debugging detection +**Tested Apps:** +- Instagram - Now works on Android 14+ +- Apps using SettingsProvider +- Apps using ContentProvider for IPC +- GeckoView/Chromium-based browsers (native engine limitation) -### Privacy Protection -- **Data Isolation**: Better separation between virtual and host environments -- **Permission Isolation**: Enhanced permission management -- **Account Separation**: Isolated account management -- **Cache Isolation**: Separate cache storage for virtual apps -- **Network Isolation**: Better network request isolation +**Test Environment:** +- Android 14 (API 34) +- Device: Various devices with Android 12+ --- -## 📱 Device Compatibility +### Future Improvements -### Android Version Support -- **Android 8.0+**: Full support with all features -- **Android 10**: Enhanced compatibility and performance -- **Android 11**: Full feature support -- **Android 12+**: Enhanced notification and permission handling -- **Android 13+**: Latest Android features supported - -### Device-Specific Support -- **Xiaomi/HyperOS**: Enhanced compatibility and security bypass -- **Samsung OneUI**: Full feature support -- **Google Pixel**: Native Android compatibility -- **OnePlus OxygenOS**: Enhanced performance and stability -- **Other Custom ROMs**: Broad compatibility support +1. **Native Engine Support:** Investigate native layer modifications to support browser engines +2. **Additional Android 14 APIs:** Monitor for new system services requiring hooks +3. **Performance:** Optimize recursive Bundle traversal for large data structures +4. **Logging:** Add debug mode to trace ContentProvider and service hook operations --- -## 🚀 New Capabilities +### Contributors -### Advanced App Management -- **Batch Operations**: Install/remove multiple apps simultaneously -- **App Cloning**: Create multiple instances of the same app -- **Data Migration**: Move app data between virtual environments -- **App Templates**: Save and restore app configurations -- **Advanced Permissions**: Granular permission management - -### Enhanced Virtualization -- **Multi-Environment Support**: Multiple virtual environments -- **Environment Isolation**: Better separation between environments -- **Resource Sharing**: Controlled resource sharing between apps -- **Performance Monitoring**: Real-time performance metrics -- **Environment Backup**: Backup and restore virtual environments - -### Developer Tools -- **API Access**: Comprehensive API for custom integrations -- **Debug Tools**: Enhanced debugging and logging capabilities -- **Performance Profiling**: App performance analysis tools -- **Custom Hooks**: Ability to add custom system hooks -- **Plugin System**: Extensible architecture for custom features +- Fixed by: AI Assistant (Antigravity) +- Tested by: alex5402 +- Date: 2026-01-30 --- -## 📋 Installation & Setup +### Upgrade Instructions -### System Requirements -- **Android Version**: 8.0+ (API 26+) -- **RAM**: Minimum 2GB, Recommended 4GB+ -- **Storage**: Minimum 2GB free space -- **Root Access**: Recommended for full functionality -- **Architecture**: ARM64, ARM32 (x86 support planned) +1. Clean build: + ```bash + ./gradlew clean assembleDebug + ``` -### Installation Steps -1. **Download APK**: Get the latest version from official sources -2. **Install APK**: Use your preferred installation method -3. **Grant Permissions**: Allow necessary permissions when prompted -4. **Initialize**: Wait for virtual environment setup to complete -5. **Configure**: Set up your preferred settings and preferences +2. Install the updated APK on your device -### First-Time Setup -- **Virtual Environment Creation**: Automatic setup of virtual environment -- **Service Initialization**: Background services are started automatically -- **Permission Configuration**: Automatic permission setup -- **WebView Configuration**: WebView system is configured automatically -- **Google Services Setup**: GMS integration is configured automatically +3. Test with previously crashing apps (e.g., Instagram on Android 14+) --- -## 🔧 Configuration Options +### Bug Reports -### General Settings -- **Debug Mode**: Enable comprehensive logging and debugging -- **Performance Mode**: Optimize for speed or battery life -- **Security Level**: Configure anti-detection features -- **Resource Limits**: Set memory and CPU usage limits -- **Auto-Update**: Enable automatic updates - -### App-Specific Settings -- **Permission Management**: Configure app permissions -- **Data Isolation**: Set data isolation levels -- **Performance Profiles**: Configure performance settings per app -- **Network Access**: Control network access for apps -- **Background Behavior**: Configure background app behavior - -### Advanced Settings -- **UID Configuration**: Custom UID management -- **Hook Configuration**: Configure system hooks -- **Service Configuration**: Customize service behavior -- **WebView Settings**: Configure WebView behavior -- **Google Services**: Configure GMS integration +If you encounter issues: +1. Capture full logcat: `adb logcat > crash.log` +2. Note the Android version and device model +3. Identify which app is crashing +4. Check for `SecurityException`, `NullPointerException`, or other errors --- -## 🐛 Known Issues & Limitations +## Summary -### Current Limitations -- **Some System Apps**: May not work with certain system applications -- **Hardware Features**: Limited support for some hardware-specific features -- **Performance Impact**: Slight performance overhead on older devices -- **Storage Usage**: Virtual environments consume additional storage -- **Battery Usage**: May increase battery consumption slightly +This release primarily focuses on **Android 14 compatibility** and **ContentProvider crash fixes**. Apps that previously crashed due to package/UID verification, AttributionSource handling, or SettingsProvider issues should now work correctly. -### Workarounds -- **System App Issues**: Use alternative apps or disable problematic features -- **Hardware Features**: Check compatibility before use -- **Performance Issues**: Enable performance mode and optimize settings -- **Storage Issues**: Regular cleanup of unused virtual environments -- **Battery Issues**: Configure power-saving options +**Breaking Changes:** None ---- +**Minimum Android Version:** Unchanged (Android 5.0+) -## 🔮 Future Roadmap - -### Version 2.5.0 (Planned) -- **Enhanced Security**: Additional anti-detection features -- **Performance Optimization**: Further performance improvements -- **Extended Compatibility**: Support for more Android versions -- **Advanced Features**: New virtualization capabilities -- **Developer Tools**: Enhanced development and debugging tools - -### Version 3.0.0 (Long-term) -- **Complete Rewrite**: Modern architecture and design -- **Cloud Integration**: Cloud-based virtual environment management -- **AI-Powered Optimization**: Intelligent performance optimization -- **Enterprise Features**: Business and enterprise capabilities -- **Cross-Platform Support**: Support for other platforms - ---- - -## 📞 Support & Community - -### Getting Help -- **Documentation**: Comprehensive user and developer documentation -- **Community Forums**: Active user community and support -- **Issue Reporting**: Bug report and feature request system -- **Developer Support**: Technical support for developers -- **Tutorials**: Step-by-step guides and tutorials - -### Contributing -- **Open Source**: Contribute to the project development -- **Bug Reports**: Help identify and report issues -- **Feature Requests**: Suggest new features and improvements -- **Documentation**: Help improve documentation -- **Testing**: Participate in testing and quality assurance - ---- - -## 📊 Performance Metrics - -### Benchmark Results -- **App Launch Time**: 15-30% faster than previous versions -- **Memory Usage**: 20-35% reduction in memory consumption -- **Battery Impact**: 10-20% reduction in battery usage -- **Storage Efficiency**: 25-40% better storage utilization -- **Stability**: 95%+ crash-free operation - -### Compatibility Statistics -- **App Success Rate**: 98%+ successful app installations -- **WebView Compatibility**: 99%+ WebView compatibility -- **Google Services**: 95%+ GMS compatibility -- **Background Jobs**: 90%+ job scheduling success -- **Overall Stability**: 97%+ system stability - ---- - -## 🎉 What's New Summary - -This release represents a **major milestone** in BlackBox development, bringing: - -✅ **Complete WebView & Browser Support** - No more browser crashes! -✅ **Enhanced Google Services** - Full GMS and account integration! -✅ **Advanced Background Jobs** - WorkManager and JobScheduler work perfectly! -✅ **Comprehensive Crash Prevention** - Stable and reliable operation! -✅ **Smart UID Management** - Intelligent system compatibility! -✅ **Enhanced Security** - Better anti-detection and privacy! -✅ **Performance Improvements** - Faster and more efficient! -✅ **Device Compatibility** - Works on more devices than ever! - ---- - -## 🚀 Upgrade Instructions - -### From Previous Versions -1. **Backup Data**: Backup your virtual environments and settings -2. **Download New Version**: Get the latest APK -3. **Install Update**: Install the new version -4. **Migrate Data**: Follow migration instructions if needed -5. **Test Features**: Verify all features work correctly - -### Fresh Installation -1. **Download APK**: Get the latest version -2. **Install**: Use your preferred method -3. **Configure**: Set up according to your preferences -4. **Test**: Verify all features work correctly - ---- - -## 🎯 Conclusion - -BlackBox 2.4.0 represents the **most comprehensive and stable** version ever released. With complete WebView support, enhanced Google services integration, advanced background job management, and comprehensive crash prevention, it provides an **enterprise-grade virtualization solution** for Android. - -**Key Benefits:** -- 🚀 **Faster & More Stable** than ever before -- 🌐 **Complete WebView Support** for all browsers and web apps -- 🔐 **Enhanced Google Services** integration -- ⚙️ **Advanced Background Jobs** with WorkManager support -- 🛡️ **Comprehensive Security** and anti-detection features -- 📱 **Better Device Compatibility** across all Android versions - -**Upgrade today** and experience the most advanced Android virtualization solution available! 🎉 - ---- - -*For detailed usage instructions, see the complete [Documentation](Docs.md)* -*For support and community, visit our [Community Forums](https://community.blackbox.com)* -*For bug reports and feature requests, use our [Issue Tracker](https://github.com/blackbox/issues)* +**Recommended Android Version:** Android 12+ for best compatibility diff --git a/ReleaseNotes.md b/ReleaseNotes.md deleted file mode 100644 index 64d3dfe..0000000 --- a/ReleaseNotes.md +++ /dev/null @@ -1,203 +0,0 @@ -# 🚀 Release Notes - v1.0.0 - -## 📱 **VSpace - Android Virtual Space Application** - -### **Release Date:** December 2024 -### **Target Android:** API 21+ (Android 5.0 Lollipop) -### **Compatibility:** Full Android 14+ (API 34) Support - ---- - -## 🎯 **What's New** - -### **✨ Major Improvements** -- **Full Android 14+ Compatibility** - Resolved all foreground service issues -- **Enhanced VPN Service Stability** - Improved network access reliability -- **Better Error Handling** - Graceful fallbacks prevent app crashes -- **Performance Optimizations** - Non-blocking service initialization - ---- - -## 🔧 **Bug Fixes & Resolutions** - -### **🚨 Critical Compilation Issues Fixed** - -#### **1. Missing Dependencies** -- **Problem:** Missing `androidx.core:core` dependency causing build failures -- **Solution:** Added proper dependency management in `gradle/libs.versions.toml` -- **Impact:** App now compiles successfully on all build environments - -#### **2. Gradle Version Compatibility** -- **Problem:** Incompatible Gradle wrapper version (8.4) with Android Gradle Plugin -- **Solution:** Downgraded to Gradle 8.2 for optimal compatibility -- **Impact:** Stable builds across different development environments - -#### **3. Build Configuration** -- **Problem:** Missing `compileSdk` and `targetSdk` declarations -- **Solution:** Added proper SDK version specifications -- **Impact:** Consistent build behavior and proper API targeting - -### **🚨 Runtime Crash Issues Fixed** - -#### **4. Foreground Service Timeout (Android 13+)** -- **Problem:** Service starting too late causing `ANR` and crashes -- **Solution:** Immediate foreground service start in `onStartCommand()` -- **Impact:** No more service timeouts or ANR dialogs - -#### **5. Missing Foreground Service Type (Android 14+)** -- **Problem:** `MissingForegroundServiceTypeException` on Android 14+ -- **Solution:** Added `FOREGROUND_SERVICE_TYPE_SPECIAL_USE` support -- **Impact:** Full Android 14+ compatibility without crashes - -#### **6. VPN Service Blocking Main Thread** -- **Problem:** VPN establishment blocking UI thread causing freezes -- **Solution:** Background thread execution for VPN operations -- **Impact:** Responsive UI during service initialization - ---- - -## 🛡️ **Technical Improvements** - -### **Service Architecture** -- **Foreground Service Compliance** - Proper Android 14+ service type handling -- **Asynchronous Operations** - Non-blocking VPN service initialization -- **Error Recovery** - Graceful fallbacks when services fail to start -- **Resource Management** - Proper service lifecycle management - -### **Permission Management** -- **Added Permissions:** - - `android.permission.FOREGROUND_SERVICE_SPECIAL_USE` - - `android.permission.FOREGROUND_SERVICE` - - `android.permission.FOREGROUND_SERVICE_MICROPHONE` - -### **Manifest Updates** -- **Service Declaration:** Added `android:foregroundServiceType="specialUse"` -- **Permission Declarations:** Proper foreground service permissions -- **Service Configuration:** Enhanced VPN service attributes - ---- - -## 📋 **System Requirements** - -### **Minimum Requirements** -- **Android Version:** 5.0 Lollipop (API 21) -- **RAM:** 2GB minimum -- **Storage:** 100MB available space -- **Permissions:** Internet, VPN, Foreground Service - -### **Recommended Requirements** -- **Android Version:** 8.0 Oreo (API 26) or higher -- **RAM:** 4GB or more -- **Storage:** 500MB available space -- **Device:** Modern Android device with good performance - ---- - -## 🔄 **Migration Guide** - -### **From Previous Versions** -1. **Clean Installation Recommended** - Due to major service architecture changes -2. **Permission Review** - New foreground service permissions may require user approval -3. **VPN Setup** - VPN service will initialize automatically on first launch - -### **Android 14+ Users** -- **Automatic Compatibility** - No manual configuration required -- **Enhanced Permissions** - Better foreground service handling -- **Improved Stability** - Resolved all Android 14+ specific issues - ---- - -## 🚀 **Performance Improvements** - -### **Service Startup** -- **Before:** 5-10 seconds with potential freezes -- **After:** 1-2 seconds with responsive UI - -### **Memory Usage** -- **Optimized:** Better resource management -- **Stable:** Reduced memory leaks and crashes - -### **Network Access** -- **Reliable:** VPN service starts consistently -- **Fast:** Immediate internet access after launch - ---- - -## 🐛 **Known Issues** - -### **None Currently Known** -- All major compilation issues resolved -- All runtime crashes fixed -- Full Android 14+ compatibility achieved - -### **Previous Issues Resolved** -- ✅ Build failures on clean environments -- ✅ Service timeout crashes -- ✅ Foreground service type exceptions -- ✅ Main thread blocking -- ✅ VPN service initialization failures - ---- - -## 🔮 **Future Enhancements** - -### **Planned Features** -- **Enhanced VPN Protocols** - Support for additional VPN types -- **Performance Monitoring** - Service health metrics -- **Advanced Permissions** - Granular permission management -- **User Experience** - Improved UI/UX for service management - -### **Technical Roadmap** -- **Service Optimization** - Further performance improvements -- **Error Reporting** - Enhanced crash analytics -- **Compatibility** - Extended Android version support -- **Security** - Enhanced permission validation - ---- - -## 📞 **Support & Feedback** - -### **Bug Reports** -- **GitHub Issues:** Report bugs via project repository -- **Logs:** Include crash logs and device information -- **Steps to Reproduce:** Detailed reproduction steps - -### **Feature Requests** -- **GitHub Discussions:** Suggest new features -- **Community Input:** Vote on proposed enhancements -- **Developer Feedback:** Direct communication channels - ---- - -## 🙏 **Acknowledgments** - -### **Contributors** -- **alex5404** - Lead developer and architect -- **Community** - Testing and feedback -- **Open Source** - Dependencies and libraries - -### **Special Thanks** -- **Android Team** - Platform improvements and documentation -- **Gradle Team** - Build system enhancements -- **Testing Community** - Bug reports and validation - ---- - -## 📄 **License** - -This project is licensed under the terms specified in the project repository. Please refer to the LICENSE file for complete details. - ---- - -## 🔗 **Links** - -- **Repository:** [GitHub Project](https://github.com/your-repo) -- **Documentation:** [Project Wiki](https://github.com/your-repo/wiki) -- **Issues:** [Bug Tracker](https://github.com/your-repo/issues) -- **Releases:** [Release History](https://github.com/your-repo/releases) - ---- - -*Last Updated: December 2024* -*Version: 1.0.0* -*Build: Stable Release* diff --git a/SECURITY_CRASH_FIXES.md b/SECURITY_CRASH_FIXES.md deleted file mode 100644 index d869b7e..0000000 --- a/SECURITY_CRASH_FIXES.md +++ /dev/null @@ -1,239 +0,0 @@ -# Security Crash Fixes for VSpace App - -## Problem Analysis - -The crash report shows a critical `SecurityException` with the message: -``` -java.lang.SecurityException: Calling uid: 10321 doesn't match source uid: 10001 -``` - -This indicates a **UID (User ID) mismatch** issue that commonly occurs in: -1. **Sandboxed environments** (like virtual spaces) -2. **Tablet devices** with different security policies -3. **Android 15** with enhanced security restrictions -4. **Xiaomi devices** with MIUI security features - -## Root Cause - -The issue occurs during application initialization when: -1. The app tries to create a package context with security-sensitive flags -2. The system detects a mismatch between the calling UID and the source UID -3. The sandboxed environment has different UID mappings than expected -4. The virtual space framework doesn't properly handle UID translation - -## Fixes Implemented - -### 1. Enhanced UID Management in NativeCore (`Bcore/src/main/java/top/niunaijun/blackbox/core/NativeCore.java`) - -**Changes Made:** -- Added comprehensive error handling around UID resolution -- Implemented fallback mechanisms for invalid UIDs -- Enhanced logging for debugging UID issues -- Added validation for UID ranges - -**Key Improvements:** -```java -@Keep -public static int getCallingUid(int origCallingUid) { - try { - // System UIDs should remain unchanged - if (origCallingUid > 0 && origCallingUid < Process.FIRST_APPLICATION_UID) - return origCallingUid; - - // Enhanced UID handling for sandboxed environments - if (origCallingUid == BlackBoxCore.getHostUid()) { - try { - int callingBUid = BActivityThread.getCallingBUid(); - if (callingBUid > 0 && callingBUid < Process.LAST_APPLICATION_UID) { - return callingBUid; - } - } catch (Exception e) { - Log.w(TAG, "Error getting calling BUid: " + e.getMessage()); - } - - // Fallback to host UID if calling BUid is invalid - return BlackBoxCore.getHostUid(); - } - return origCallingUid; - } catch (Exception e) { - Log.e(TAG, "Error in getCallingUid: " + e.getMessage()); - return Process.myUid(); // Safe fallback - } -} -``` - -### 2. Enhanced Application Creation in BActivityThread (`Bcore/src/main/java/top/niunaijun/blackbox/app/BActivityThread.java`) - -**Changes Made:** -- Implemented multiple fallback methods for application creation -- Added specific handling for SecurityException -- Enhanced package context creation with alternative approaches -- Improved provider installation with error handling - -**Key Improvements:** -```java -private Application createApplicationWithFallback(Object loadedApk, Context packageContext, String packageName) { - Application application = null; - - // Method 1: Standard makeApplication - try { - application = BRLoadedApk.get(loadedApk).makeApplication(false, null); - if (application != null) return application; - } catch (Exception e) { - Slog.w(TAG, "Standard makeApplication failed: " + e.getMessage()); - } - - // Method 2: Alternative parameters - try { - application = BRLoadedApk.get(loadedApk).makeApplication(true, null); - if (application != null) return application; - } catch (Exception e) { - Slog.w(TAG, "Alternative makeApplication failed: " + e.getMessage()); - } - - // Method 3: Minimal application wrapper - try { - application = createMinimalApplication(packageContext, packageName); - if (application != null) return application; - } catch (Exception e) { - Slog.w(TAG, "Minimal application creation failed: " + e.getMessage()); - } - - return null; -} -``` - -### 3. Enhanced Package Context Creation - -**Changes Made:** -- Added fallback context creation without security flags -- Implemented specific SecurityException handling -- Added alternative context creation methods - -**Key Improvements:** -```java -public static Context createPackageContext(ApplicationInfo info) { - try { - return BlackBoxCore.getContext().createPackageContext(info.packageName, - Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); - } catch (SecurityException se) { - Slog.e(TAG, "SecurityException creating package context for " + info.packageName); - // Try alternative approach for sandboxed environments - try { - return BlackBoxCore.getContext().createPackageContext(info.packageName, - Context.CONTEXT_INCLUDE_CODE); - } catch (Exception e2) { - Slog.e(TAG, "Alternative package context creation also failed: " + e2.getMessage()); - } - } catch (Exception e) { - Slog.e(TAG, "Error creating package context for " + info.packageName + ": " + e.getMessage()); - } - return null; -} -``` - -### 4. Enhanced BlackBoxCore UID Management (`Bcore/src/main/java/top/niunaijun/blackbox/BlackBoxCore.java`) - -**Changes Made:** -- Added sandboxed environment detection -- Implemented enhanced UID resolution for security-sensitive operations -- Added current app UID tracking -- Enhanced service initialization with security exception handling - -**Key Improvements:** -```java -// Enhanced UID management for sandboxed environments -private int mCurrentAppUid = -1; -private String mCurrentAppPackage = null; -private boolean mIsSandboxedEnvironment = false; - -public int resolveUidForOperation(int originalUid, String operation) { - try { - // System UIDs should remain unchanged - if (originalUid > 0 && originalUid < Process.FIRST_APPLICATION_UID) { - return originalUid; - } - - // If we're in a sandboxed environment, use the current app UID - if (mIsSandboxedEnvironment && mCurrentAppUid > 0) { - Slog.d("BlackBoxCore", "Resolving UID for " + operation + ": " + originalUid + " -> " + mCurrentAppUid); - return mCurrentAppUid; - } - - return originalUid; - } catch (Exception e) { - Slog.e("BlackBoxCore", "Error resolving UID for " + operation + ": " + e.getMessage()); - return originalUid; - } -} -``` - -### 5. Security Exception Recovery - -**Changes Made:** -- Implemented specific SecurityException handling in application creation -- Added fallback application creation methods -- Enhanced error recovery mechanisms -- Improved logging for security-related issues - -**Key Improvements:** -```java -private void handleSecurityException(SecurityException se, String packageName, String processName, Context packageContext) { - Slog.w(TAG, "Handling SecurityException for " + packageName); - - // Try to create a basic application without problematic operations - try { - Application basicApp = createMinimalApplication(packageContext, packageName); - if (basicApp != null) { - mInitialApplication = basicApp; - BRActivityThread.get(BlackBoxCore.mainThread())._set_mInitialApplication(mInitialApplication); - ContextCompat.fix(mInitialApplication); - - // Skip problematic operations - Slog.w(TAG, "Created basic application, skipping problematic operations"); - return; - } - } catch (Exception e) { - Slog.e(TAG, "Failed to create basic application after SecurityException: " + e.getMessage()); - } - - throw new RuntimeException("Unable to handle SecurityException", se); -} -``` - -## Expected Results - -After implementing these fixes, the app should: - -1. **Handle UID mismatches gracefully** without crashing -2. **Work properly on tablets** with different security policies -3. **Support Android 15** security restrictions -4. **Function in sandboxed environments** like virtual spaces -5. **Provide fallback mechanisms** when security operations fail -6. **Maintain functionality** even with limited permissions - -## Testing Recommendations - -1. **Test on Xiaomi tablets** with MIUI -2. **Test on Android 15 devices** -3. **Test in virtual space environments** -4. **Test with different app permissions** -5. **Monitor logcat** for security-related messages -6. **Verify UID resolution** in different scenarios - -## Additional Notes - -- The fixes maintain backward compatibility -- Security exceptions are handled gracefully without breaking functionality -- Enhanced logging helps with debugging security issues -- Fallback mechanisms ensure the app continues to work even with limited permissions -- The app will adapt to different security environments automatically - -## Build Instructions - -1. Clean and rebuild the project in Android Studio -2. Test on the target Xiaomi tablet with Android 15 -3. Monitor logcat for any remaining security issues -4. Verify that the SecurityException crash is resolved - -The comprehensive security handling implemented should resolve the UID mismatch crashes while maintaining the app's functionality across different devices and Android versions. diff --git a/SOCIAL_MEDIA_CRASH_FIXES.md b/SOCIAL_MEDIA_CRASH_FIXES.md deleted file mode 100644 index 2834619..0000000 --- a/SOCIAL_MEDIA_CRASH_FIXES.md +++ /dev/null @@ -1,308 +0,0 @@ -# Social Media App Crash Fixes for Bcore - -## Overview - -This document describes the comprehensive fixes implemented to prevent crashes in social media apps like Facebook, Instagram, WhatsApp, and others when running in the BlackBox virtual environment. - -## Problem Analysis - -The main issues causing crashes in social media apps were: - -1. **AttributionSource UID Mismatches** - Android 12+ enforces strict UID validation -2. **WebView Data Directory Conflicts** - Multiple virtual apps sharing WebView data -3. **Context and Resource Access Issues** - Null context crashes during app initialization -4. **Xiaomi-specific Security Enforcement** - Strict UID validation on MIUI devices -5. **Insufficient Error Handling** - Many system calls not handling exceptions gracefully -6. **Missing Crash Prevention Mechanisms** - No comprehensive crash handling - -## Implemented Fixes - -### 1. Enhanced IActivityManagerProxy - -**File**: `IActivityManagerProxy.java` - -- Added comprehensive error handling for all ActivityManager calls -- Prevents SecurityException crashes by returning safe default values -- Enhanced `getContentProvider`, `startActivity`, `startService`, and `stopService` methods -- Graceful fallback for permission-related operations - -**Key Changes**: -```java -@Override -public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - try { - return super.invoke(proxy, method, args); - } catch (SecurityException e) { - // Return appropriate default values based on method type - String methodName = method.getName(); - if (methodName.startsWith("set") || methodName.startsWith("update")) { - return null; // Success - } else if (methodName.startsWith("get") || methodName.startsWith("query")) { - return null; // Empty result - } - // ... more fallback logic - } -} -``` - -### 2. Enhanced WebView Proxy - -**File**: `WebViewProxy.java` - -- Prevents WebView data directory conflicts between virtual apps -- Creates unique data directories for each virtual app and process -- Handles WebView initialization failures gracefully -- Provides fallback WebView creation when normal initialization fails - -**Key Features**: -- Unique data directory per virtual app: `/data/data/{package}/webview_{userId}_{pid}` -- Automatic directory creation and permission handling -- Fallback WebView with basic configuration -- Enhanced error handling for all WebView operations - -### 3. Enhanced AttributionSource Utils - -**File**: `AttributionSourceUtils.java` - -- Comprehensive UID fixing for AttributionSource objects -- Handles multiple field names and setter methods -- Fixes AttributionSource objects in Bundle objects -- Creates safe fallback AttributionSource objects -- Added validation methods for AttributionSource integrity - -**Key Methods**: -```java -public static void fixAttributionSourceInArgs(Object[] args) -public static void fixAttributionSourceUid(Object attributionSource) -public static Object createSafeAttributionSource() -public static boolean validateAttributionSource(Object attributionSource) -``` - -### 4. Enhanced SimpleCrashFix - -**File**: `SimpleCrashFix.java` - -- Global exception handler for all types of crashes -- Specific handling for WebView, AttributionSource, and social media app crashes -- Prevents crashes from propagating to the system -- Context wrapper hooks to prevent null context issues - -**Crash Types Handled**: -- Null context crashes -- Google Play Services crashes -- WebView crashes -- AttributionSource crashes -- Social media app specific crashes - -### 5. Enhanced HookManager - -**File**: `HookManager.java` - -- Better error handling for hook failures -- Critical hook recovery mechanisms -- Hook status monitoring -- Force re-initialization capabilities - -**Key Features**: -```java -private void handleHookError(IInjectHook hook, Exception e) -public boolean areCriticalHooksInstalled() -public void reinitializeHooks() -``` - -### 6. Social Media App Crash Prevention - -**File**: `SocialMediaAppCrashPrevention.java` - -- Comprehensive crash prevention specifically for social media apps -- Automatic detection of social media app packages -- Specialized handling for Facebook, Instagram, WhatsApp, etc. -- WebView, context, and permission crash prevention - -**Supported Apps**: -- Facebook & Messenger -- Instagram -- WhatsApp -- Telegram -- Twitter/X -- TikTok -- Snapchat -- YouTube -- LinkedIn -- Discord -- Reddit -- Spotify -- Netflix -- Prime Video - -## Installation and Usage - -### Automatic Installation - -The crash prevention mechanisms are automatically installed when BlackBoxCore is initialized: - -```java -// In BlackBoxCore static initializer -static { - SimpleCrashFix.installSimpleFix(); - StackTraceFilter.install(); - SocialMediaAppCrashPrevention.initialize(); -} -``` - -### Manual Initialization - -If needed, you can manually initialize crash prevention: - -```java -// Initialize all crash prevention mechanisms -SocialMediaAppCrashPrevention.initialize(); - -// Check status -String status = SocialMediaAppCrashPrevention.getCrashPreventionStatus(); -Slog.d(TAG, status); -``` - -### Hook Management - -```java -// Check if critical hooks are installed -HookManager hookManager = HookManager.get(); -boolean hooksOk = hookManager.areCriticalHooksInstalled(); - -// Force re-initialization if needed -if (!hooksOk) { - hookManager.reinitializeHooks(); -} -``` - -## Configuration - -### WebView Configuration - -WebView data directories are automatically configured with unique paths: - -``` -/data/data/{package}/webview_{userId}_{pid}/ -├── cache/ -├── cookies/ -└── databases/ -``` - -### AttributionSource Configuration - -UID and package name are automatically fixed: - -```java -// UID is set to the virtual app's UID -// Package name is set to the host package -AttributionSourceUtils.fixAttributionSourceUid(attributionSource); -``` - -## Monitoring and Debugging - -### Log Tags - -Use these log tags to monitor crash prevention: - -- `SimpleCrashFix` - General crash prevention -- `SocialMediaCrashPrevention` - Social media specific fixes -- `WebViewProxy` - WebView crash prevention -- `AttributionSourceUtils` - AttributionSource fixes -- `HookManager` - Hook management and recovery - -### Status Checking - -```java -// Get comprehensive status -String status = SocialMediaAppCrashPrevention.getCrashPreventionStatus(); -Slog.d(TAG, status); - -// Check specific app -boolean isSocialMedia = SocialMediaAppCrashPrevention.isSocialMediaApp(); -``` - -## Performance Impact - -The crash prevention mechanisms have minimal performance impact: - -- **Memory**: ~2-5MB additional memory usage -- **CPU**: Negligible CPU overhead during normal operation -- **Startup**: ~100-200ms additional startup time -- **Runtime**: No measurable impact on app performance - -## Compatibility - -### Android Versions - -- **Android 5.0+ (API 21+)**: Full support -- **Android 10+ (API 29+)**: Enhanced WebView support -- **Android 12+ (API 31+)**: Full AttributionSource support - -### Device Manufacturers - -- **Xiaomi/MIUI**: Enhanced support with Xiaomi-specific proxies -- **Samsung**: Full support -- **Huawei**: Full support -- **OnePlus**: Full support -- **Google**: Full support - -## Troubleshooting - -### Common Issues - -1. **WebView still crashes** - - Check if WebViewProxy is properly installed - - Verify data directory permissions - - Check logcat for specific error messages - -2. **AttributionSource errors persist** - - Ensure AttributionSourceUtils is working - - Check if UID is properly set - - Verify package name is correct - -3. **Hooks not working** - - Check HookManager status - - Force re-initialization - - Verify critical hooks are installed - -### Debug Commands - -```bash -# Check logs for crash prevention -adb logcat | grep -E "(SimpleCrashFix|SocialMediaCrashPrevention|WebViewProxy)" - -# Check WebView directories -adb shell ls -la /data/data/*/webview_* - -# Check system properties -adb shell getprop | grep webview -``` - -## Future Enhancements - -1. **Machine Learning Crash Prediction** - Predict crashes before they happen -2. **Dynamic Hook Optimization** - Optimize hooks based on app behavior -3. **Enhanced Xiaomi Support** - Better MIUI compatibility -4. **Performance Profiling** - Detailed performance impact analysis -5. **Automated Testing** - Automated crash prevention testing - -## Support - -For issues or questions regarding these crash fixes: - -1. Check the logs for specific error messages -2. Verify all crash prevention mechanisms are initialized -3. Test with a clean virtual app installation -4. Report issues with detailed logs and device information - -## Conclusion - -These comprehensive crash fixes should resolve the black screen and crash issues in social media apps when running in the BlackBox virtual environment. The fixes address the root causes while maintaining compatibility and performance. - -The implementation follows best practices for: -- Error handling and recovery -- Performance optimization -- Compatibility across Android versions -- Device-specific optimizations -- Comprehensive logging and monitoring diff --git a/SYNTAX_ERROR_FIX.md b/SYNTAX_ERROR_FIX.md deleted file mode 100644 index d798a5d..0000000 --- a/SYNTAX_ERROR_FIX.md +++ /dev/null @@ -1,155 +0,0 @@ -# Syntax Error Fix - Finally Block Placement - -## Problem Analysis - -The build was failing with multiple compilation errors due to improper placement of the `finally` block: - -``` -error: illegal start of type - } finally { - ^ -error: class, interface, or enum expected - private PackageInfo createFallbackPackageInfo(String packageName, int flags, int userId) { - ^ -``` - -**Root Cause:** The `finally` block was not properly structured within a `try` block, causing syntax errors. - -## Issue Details - -### Location of Error -- **File**: `Bcore/src/main/java/top/niunaijun/blackbox/fake/frameworks/BPackageManager.java` -- **Line**: 721 -- **Method**: `findActualApkPath` - -### Problem Structure -```java -private String findActualApkPath(String packageName) { - if (sIsFindingApkPath) { - return null; - } - sIsFindingApkPath = true; - - // Code here... - - return null; - } finally { // ERROR: finally without try - sIsFindingApkPath = false; - } -} -``` - -## Fix Applied - -### Before (Error) -```java -private String findActualApkPath(String packageName) { - if (sIsFindingApkPath) { - Log.w(TAG, "findActualApkPath called recursively, returning null to prevent infinite loop."); - return null; - } - sIsFindingApkPath = true; - - // Skip PackageManager call to prevent infinite recursion - Log.d(TAG, "Skipping PackageManager call to prevent recursion for " + packageName); - - // Try common paths - String[] commonPaths = { - "/data/app/" + packageName + "-1/base.apk", - "/data/app/" + packageName + "-2/base.apk", - "/data/app/" + packageName + "/base.apk", - "/system/app/" + packageName + ".apk", - "/system/priv-app/" + packageName + ".apk" - }; - - for (String path : commonPaths) { - if (new File(path).exists()) { - Log.d(TAG, "Found existing APK at: " + path); - return path; - } - } - - Log.w(TAG, "No existing APK found for " + packageName + ", using fallback path"); - return null; - } finally { // ERROR: finally without try - sIsFindingApkPath = false; - } -} -``` - -### After (Fixed) -```java -private String findActualApkPath(String packageName) { - if (sIsFindingApkPath) { - Log.w(TAG, "findActualApkPath called recursively, returning null to prevent infinite loop."); - return null; - } - sIsFindingApkPath = true; - try { - // Skip PackageManager call to prevent infinite recursion - Log.d(TAG, "Skipping PackageManager call to prevent recursion for " + packageName); - - // Try common paths - String[] commonPaths = { - "/data/app/" + packageName + "-1/base.apk", - "/data/app/" + packageName + "-2/base.apk", - "/data/app/" + packageName + "/base.apk", - "/system/app/" + packageName + ".apk", - "/system/priv-app/" + packageName + ".apk" - }; - - for (String path : commonPaths) { - if (new File(path).exists()) { - Log.d(TAG, "Found existing APK at: " + path); - return path; - } - } - - Log.w(TAG, "No existing APK found for " + packageName + ", using fallback path"); - return null; - } finally { - sIsFindingApkPath = false; // Reset flag - } -} -``` - -## Solution Explanation - -### Why This Fix Works -1. **Proper Try-Finally Structure**: The `finally` block is now properly contained within a `try` block -2. **Flag Management**: The `sIsFindingApkPath` flag is properly reset in the `finally` block -3. **Exception Safety**: The flag will be reset even if an exception occurs -4. **Syntax Compliance**: The code now follows Java syntax rules - -### Key Changes -1. **Added `try` block**: Wrapped the main logic in a try block -2. **Proper `finally` placement**: Moved the finally block inside the try block -3. **Maintained functionality**: All the original logic remains intact -4. **Flag reset guarantee**: The flag is guaranteed to be reset - -## Expected Results - -After applying this fix: - -✅ **All compilation errors resolved** -✅ **Proper try-finally structure** -✅ **Flag management works correctly** -✅ **Recursion protection maintained** -✅ **No impact on existing functionality** - -## Testing - -1. **Build the project** in Android Studio -2. **Verify no compilation errors** -3. **Test app startup** on target devices -4. **Monitor logcat** for recursion protection messages -5. **Verify flag reset** works correctly - -## Additional Notes - -- This fix is part of the larger infinite recursion protection implementation -- The try-finally structure ensures proper resource cleanup -- The flag management prevents infinite recursion in PackageManager hooks -- The fix maintains all existing functionality while fixing the syntax error - -The syntax error has been resolved and the project should now compile successfully. diff --git a/XIAOMI_CRASH_FIXES.md b/XIAOMI_CRASH_FIXES.md deleted file mode 100644 index 9e5c287..0000000 --- a/XIAOMI_CRASH_FIXES.md +++ /dev/null @@ -1,313 +0,0 @@ -# 🚀 Xiaomi Device Crash Fixes - Complete Implementation - -## 📋 Overview - -This document outlines the comprehensive solution implemented to fix crashes on Xiaomi devices running MIUI. The crashes were primarily caused by **UID mismatch security exceptions** when accessing system services, which is more strictly enforced on Xiaomi devices compared to stock Android. - -## 🚨 Root Causes Identified - -### 1. **Primary Issue: UID Mismatch SecurityException** -``` -java.lang.SecurityException: Calling uid: 10805 doesn't match source uid: 10000 -``` - -**Why This Happens on Xiaomi:** -- **Enhanced Security Enforcement**: MIUI has stricter security policies than stock Android -- **AttributionSource Validation**: More aggressive UID checking in system services -- **Android 12+ Compatibility**: Stricter enforcement of AttributionSource security features - -### 2. **Secondary Issues: Hook Failures** -Multiple proxy hooks were failing during initialization: -- `SystemLibraryProxy` -- `IPersistentDataBlockServiceProxy` -- `SQLiteDatabaseProxy` -- `MediaRecorderClassProxy` -- `LevelDbProxy` -- `GmsProxy` - -### 3. **MIUI-Specific Issues** -- `MiuiCameraCoveredManager` NullPointerException -- `MiuiForceDarkConfig` crashes -- `MiuiMonitorThread` stability issues -- `OplusOverScrollerExtImpl` UID mismatch - -## 🔧 Solutions Implemented - -### **1. IXiaomiAttributionSourceProxy** -**Purpose**: Global AttributionSource UID mismatch prevention - -**Features**: -- Hooks `AttributionSource` constructor calls -- Bypasses `enforceCallingUid` and `enforceCallingUidAndPid` -- Fixes UID issues in `fromParcel` deserialization -- Creates safe fallback AttributionSource objects - -**Key Methods**: -```java -@ProxyMethod("AttributionSource") - Constructor hook -@ProxyMethod("enforceCallingUid") - UID enforcement bypass -@ProxyMethod("enforceCallingUidAndPid") - UID enforcement bypass -@ProxyMethod("fromParcel") - Deserialization fix -``` - -### **2. IXiaomiSettingsProxy** -**Purpose**: MIUI Settings access UID mismatch prevention - -**Features**: -- Hooks `Settings.System` and `Settings.Global` methods -- Provides safe defaults for UID mismatch scenarios -- Fixes AttributionSource in method arguments - -**Key Methods**: -```java -@ProxyMethod("getStringForUser") - String retrieval with UID fix -@ProxyMethod("getString") - String retrieval with UID fix -@ProxyMethod("getIntForUser") - Integer retrieval with UID fix -@ProxyMethod("getInt") - Integer retrieval with UID fix -``` - -### **3. IXiaomiContentProviderProxy** -**Purpose**: ContentProvider UID mismatch prevention - -**Features**: -- Hooks all major ContentProvider operations -- Provides safe return values for crashes -- Fixes AttributionSource in method arguments - -**Key Methods**: -```java -@ProxyMethod("query") - Query operations with UID fix -@ProxyMethod("insert") - Insert operations with UID fix -@ProxyMethod("update") - Update operations with UID fix -@ProxyMethod("delete") - Delete operations with UID fix -@ProxyMethod("call") - Call operations with UID fix -@ProxyMethod("getType") - Type retrieval with UID fix -``` - -### **4. IXiaomiMiuiServicesProxy** -**Purpose**: MIUI-specific service crash prevention - -**Features**: -- Hooks MIUI framework services -- Prevents NullPointerException in camera manager -- Handles MIUI-specific UID issues - -**Key Methods**: -```java -@ProxyMethod("MiuiCameraCoveredManager") - Camera manager crash prevention -@ProxyMethod("MiuiForceDarkConfig") - Force dark config crash prevention -@ProxyMethod("MiuiMonitorThread") - Monitor thread crash prevention -@ProxyMethod("OplusOverScrollerExtImpl") - Oplus framework UID fix -``` - -### **5. XiaomiDeviceDetector** -**Purpose**: Device detection and MIUI version identification - -**Features**: -- Detects Xiaomi devices by multiple criteria -- Identifies MIUI version and Android version -- Provides device-specific information for debugging - -**Detection Methods**: -- Manufacturer check (`Build.MANUFACTURER`) -- Brand check (`Build.BRAND`) -- Model check (`Build.MODEL`) -- Product check (`Build.PRODUCT`) -- Device check (`Build.DEVICE`) -- Fingerprint check (`Build.FINGERPRINT`) -- System properties check (`ro.miui.ui.version.name`) - -## 🎯 How the Fixes Work - -### **1. Proactive UID Fixing** -Before calling any system method, the proxies: -1. **Scan method arguments** for AttributionSource objects -2. **Fix UID values** using reflection to set correct UIDs -3. **Fix package names** to match the sandbox environment -4. **Handle Bundle objects** that may contain AttributionSource - -### **2. Exception Handling** -When UID mismatches occur: -1. **Catch SecurityException** with UID mismatch messages -2. **Log the issue** for debugging purposes -3. **Return safe defaults** instead of crashing -4. **Create fallback objects** when possible - -### **3. Device-Specific Logic** -The proxies: -1. **Detect Xiaomi devices** automatically -2. **Apply MIUI-specific fixes** based on device type -3. **Handle different MIUI versions** appropriately -4. **Provide detailed logging** for troubleshooting - -## 📱 Device Compatibility - -### **Supported Xiaomi Brands**: -- **Xiaomi** (Mi series) -- **Redmi** (Redmi series) -- **POCO** (POCO series) -- **Black Shark** (Gaming series) -- **Mi** (Legacy series) - -### **Supported MIUI Versions**: -- **MIUI 12+** (Android 10+) -- **MIUI 13+** (Android 11+) -- **MIUI 14+** (Android 12+) -- **HyperOS** (Android 13+, MIUI 15+) - -### **Supported Android Versions**: -- **Android 10** (API 29) - MIUI 12 -- **Android 11** (API 30) - MIUI 12.5 -- **Android 12** (API 31) - MIUI 13 -- **Android 13** (API 33) - MIUI 14 -- **Android 14** (API 34) - HyperOS - -## 🚀 Implementation Steps - -### **1. Proxy Registration** -All Xiaomi proxies are automatically registered in `HookManager.init()`: -```java -// Xiaomi-specific proxies to prevent crashes on MIUI devices -addInjector(new IXiaomiAttributionSourceProxy()); -addInjector(new IXiaomiSettingsProxy()); -addInjector(new IXiaomiContentProviderProxy()); -addInjector(new IXiaomiMiuiServicesProxy()); -``` - -### **2. Device Detection** -The system automatically detects Xiaomi devices and applies appropriate fixes: -```java -if (XiaomiDeviceDetector.isXiaomiDevice()) { - // Apply Xiaomi-specific fixes - Slog.d(TAG, "Xiaomi device detected: " + XiaomiDeviceDetector.getDeviceInfo()); -} -``` - -### **3. Automatic UID Fixing** -All system calls are automatically intercepted and fixed: -```java -// Fix AttributionSource in args before calling original method -fixAttributionSourceInArgs(args); - -// Call original method with fixed arguments -return method.invoke(who, args); -``` - -## 🔍 Debugging and Monitoring - -### **Log Tags**: -- `IXiaomiAttributionSourceProxy` -- `IXiaomiSettingsProxy` -- `IXiaomiContentProviderProxy` -- `IXiaomiMiuiServicesProxy` -- `XiaomiDeviceDetector` - -### **Key Log Messages**: -``` -"Xiaomi device detected by manufacturer: Xiaomi" -"Detected MIUI version: MIUI 14.0.1" -"Fixed Xiaomi AttributionSource UID via field: mUid" -"Xiaomi UID mismatch in getStringForUser, returning safe default" -"MIUI Camera Manager NullPointerException on Xiaomi, creating safe fallback" -``` - -### **Device Information Output**: -``` -Device: Xiaomi Redmi Note 12 -Android: 13 (API 33) -Xiaomi Device: true -MIUI Version: MIUI 14.0.1 -MIUI 12+: true -MIUI 13+: true -HyperOS: false -``` - -## 📊 Expected Results - -### **Before Fixes**: -- **80% crash rate** on Xiaomi devices -- **UID mismatch crashes** in system services -- **MIUI framework crashes** in proprietary services -- **Black screen issues** after crashes - -### **After Fixes**: -- **<5% crash rate** on Xiaomi devices -- **No UID mismatch crashes** in system services -- **Stable MIUI framework** operation -- **Smooth app operation** without black screens - -## 🧪 Testing Recommendations - -### **Test Devices**: -1. **Xiaomi Mi 13** (MIUI 14, Android 13) -2. **Redmi Note 12** (MIUI 14, Android 13) -3. **POCO X5** (MIUI 14, Android 13) -4. **Black Shark 5** (MIUI 13, Android 12) - -### **Test Scenarios**: -1. **App Launch** - Verify no crashes during startup -2. **Settings Access** - Test system settings retrieval -3. **ContentProvider Calls** - Test database operations -4. **MIUI Services** - Test camera and display features -5. **Long-term Stability** - Test continuous operation - -### **Monitoring**: -1. **Check logs** for Xiaomi-specific messages -2. **Monitor crash rates** on different MIUI versions -3. **Verify UID fixes** are being applied -4. **Test fallback mechanisms** work correctly - -## 🔮 Future Enhancements - -### **Planned Improvements**: -1. **MIUI Version-Specific Fixes** - Tailored solutions for different MIUI versions -2. **Performance Optimization** - Reduce overhead of UID fixing -3. **Enhanced Detection** - Better device and service detection -4. **Automated Testing** - CI/CD integration for Xiaomi devices - -### **Additional Services**: -1. **MIUI Security Center** - Handle security-related crashes -2. **MIUI Battery Manager** - Fix battery optimization issues -3. **MIUI Theme Engine** - Handle theme-related crashes -4. **MIUI Permission Manager** - Fix permission-related issues - -## 📞 Support and Troubleshooting - -### **Common Issues**: -1. **Proxy not loading** - Check HookManager registration -2. **Device not detected** - Verify Build properties -3. **UID fixes not working** - Check reflection access -4. **Performance impact** - Monitor method call overhead - -### **Debug Commands**: -```bash -# Check device detection -adb logcat | grep "XiaomiDeviceDetector" - -# Monitor UID fixes -adb logcat | grep "Fixed Xiaomi AttributionSource UID" - -# Check crash prevention -adb logcat | grep "Xiaomi UID mismatch.*returning safe default" -``` - -### **Contact Information**: -- **Developer**: BlackBox Framework Team -- **Repository**: vspace-fully-fixed -- **Issue Tracking**: GitHub Issues -- **Documentation**: This file and inline code comments - ---- - -## 🎉 Conclusion - -This comprehensive solution addresses the root causes of Xiaomi device crashes by implementing: - -1. **Proactive UID fixing** before system calls -2. **Exception handling** with safe fallbacks -3. **Device-specific logic** for MIUI compatibility -4. **Comprehensive coverage** of all crash scenarios - -The implementation is **automatic**, **transparent**, and **efficient**, requiring no user intervention while providing robust crash prevention on all supported Xiaomi devices and MIUI versions. - -**Result**: Stable operation on Xiaomi devices with <5% crash rate compared to the previous 80% crash rate. diff --git a/app/src/main/java/top/niunaijun/blackboxa/view/main/BlackBoxLoader.kt b/app/src/main/java/top/niunaijun/blackboxa/view/main/BlackBoxLoader.kt index bb7fce4..c66b4b6 100644 --- a/app/src/main/java/top/niunaijun/blackboxa/view/main/BlackBoxLoader.kt +++ b/app/src/main/java/top/niunaijun/blackboxa/view/main/BlackBoxLoader.kt @@ -147,6 +147,26 @@ class BlackBoxLoader { Log.e(TAG, "Error in afterApplicationOnCreate: ${e.message}") } } + + override fun onStoragePermissionNeeded(packageName: String?, userId: Int): Boolean { + try { + Log.w(TAG, "Storage permission needed for launching: $packageName") + // Broadcast to request storage permission + // The main activity should listen for this and show permission dialog + val intent = android.content.Intent("top.niunaijun.blackboxa.REQUEST_STORAGE_PERMISSION") + intent.putExtra("package_name", packageName) + intent.putExtra("user_id", userId) + intent.setPackage(App.getContext().packageName) + App.getContext().sendBroadcast(intent) + // Return false to NOT block the launch - the app will launch anyway + // but the user will be notified to grant permission + // Change to 'true' if you want to block launch until permission is granted + return false + } catch (e: Exception) { + Log.e(TAG, "Error in onStoragePermissionNeeded: ${e.message}") + return false + } + } }) } catch (e: Exception) { Log.e(TAG, "Error adding lifecycle callback: ${e.message}") diff --git a/app/src/main/java/top/niunaijun/blackboxa/view/main/MainActivity.kt b/app/src/main/java/top/niunaijun/blackboxa/view/main/MainActivity.kt index 1e213a9..1f83ba4 100644 --- a/app/src/main/java/top/niunaijun/blackboxa/view/main/MainActivity.kt +++ b/app/src/main/java/top/niunaijun/blackboxa/view/main/MainActivity.kt @@ -25,7 +25,6 @@ import top.niunaijun.blackboxa.view.fake.FakeManagerActivity import top.niunaijun.blackboxa.view.list.ListActivity import top.niunaijun.blackboxa.view.setting.SettingActivity - class MainActivity : LoadingActivity() { private val viewBinding: ActivityMainBinding by inflate() @@ -38,7 +37,7 @@ class MainActivity : LoadingActivity() { companion object { private const val TAG = "MainActivity" - + fun start(context: Context) { val intent = Intent(context, MainActivity::class.java) context.startActivity(intent) @@ -61,6 +60,9 @@ class MainActivity : LoadingActivity() { initFab() initToolbarSubTitle() + // Check and request storage permission on Android 11+ + checkStoragePermission() + try { BlackBoxCore.get().onAfterMainActivityOnCreate(this) } catch (e: Exception) { @@ -73,14 +75,81 @@ class MainActivity : LoadingActivity() { } } + private fun checkStoragePermission() { + try { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + if (!android.os.Environment.isExternalStorageManager()) { + Log.w(TAG, "MANAGE_EXTERNAL_STORAGE permission not granted") + showStoragePermissionDialog() + } + } + } catch (e: Exception) { + Log.e(TAG, "Error checking storage permission: ${e.message}") + } + } + + private fun showStoragePermissionDialog() { + try { + MaterialDialog(this).show { + title(text = "Storage Permission Required") + message( + text = + "This app needs 'All Files Access' permission to properly run sandboxed apps. Without this permission, some apps may not work correctly.\n\nPlease grant permission in the next screen." + ) + positiveButton(text = "Grant Permission") { openAllFilesAccessSettings() } + negativeButton(text = "Later") { Log.w(TAG, "User postponed storage permission") } + cancelable(false) + } + } catch (e: Exception) { + Log.e(TAG, "Error showing storage permission dialog: ${e.message}") + } + } + + private fun openAllFilesAccessSettings() { + try { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + val intent = + Intent( + android.provider.Settings + .ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION + ) + intent.data = Uri.parse("package:$packageName") + storagePermissionResult.launch(intent) + } + } catch (e: Exception) { + Log.e(TAG, "Error opening storage settings: ${e.message}") + // Fallback: open general manage all files settings + try { + val intent = + Intent(android.provider.Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION) + storagePermissionResult.launch(intent) + } catch (e2: Exception) { + Log.e(TAG, "Error opening fallback storage settings: ${e2.message}") + } + } + } + + private val storagePermissionResult = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + try { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { + if (android.os.Environment.isExternalStorageManager()) { + Log.d(TAG, "Storage permission granted!") + } else { + Log.w(TAG, "Storage permission still not granted") + } + } + } catch (e: Exception) { + Log.e(TAG, "Error handling storage permission result: ${e.message}") + } + } + private fun showErrorDialog(message: String) { try { MaterialDialog(this).show { title(text = "Error") message(text = message) - positiveButton(text = "OK") { - finish() - } + positiveButton(text = "OK") { finish() } } } catch (e: Exception) { Log.e(TAG, "Error showing error dialog: ${e.message}") @@ -91,14 +160,14 @@ class MainActivity : LoadingActivity() { private fun initToolbarSubTitle() { try { updateUserRemark(0) - //hack code + // hack code viewBinding.toolbarLayout.toolbar.getChildAt(1)?.setOnClickListener { try { MaterialDialog(this).show { title(res = R.string.userRemark) input( - hintRes = R.string.userRemark, - prefill = viewBinding.toolbarLayout.toolbar.subtitle + hintRes = R.string.userRemark, + prefill = viewBinding.toolbarLayout.toolbar.subtitle ) { _, input -> try { AppManager.mRemarkSharedPreferences.edit { @@ -124,9 +193,7 @@ class MainActivity : LoadingActivity() { private fun initViewPager() { try { val userList = BlackBoxCore.get().users - userList.forEach { - fragmentList.add(AppsFragment.newInstance(it.id)) - } + userList.forEach { fragmentList.add(AppsFragment.newInstance(it.id)) } currentUser = userList.firstOrNull()?.id ?: 0 fragmentList.add(AppsFragment.newInstance(userList.size)) @@ -135,19 +202,20 @@ class MainActivity : LoadingActivity() { mViewPagerAdapter.replaceData(fragmentList) viewBinding.viewPager.adapter = mViewPagerAdapter viewBinding.dotsIndicator.setViewPager2(viewBinding.viewPager) - viewBinding.viewPager.registerOnPageChangeCallback(object : - ViewPager2.OnPageChangeCallback() { - override fun onPageSelected(position: Int) { - try { - super.onPageSelected(position) - currentUser = fragmentList[position].userID - updateUserRemark(currentUser) - showFloatButton(true) - } catch (e: Exception) { - Log.e(TAG, "Error in onPageSelected: ${e.message}") + viewBinding.viewPager.registerOnPageChangeCallback( + object : ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + try { + super.onPageSelected(position) + currentUser = fragmentList[position].userID + updateUserRemark(currentUser) + showFloatButton(true) + } catch (e: Exception) { + Log.e(TAG, "Error in onPageSelected: ${e.message}") + } + } } - } - }) + ) } catch (e: Exception) { Log.e(TAG, "Error in initViewPager: ${e.message}") } @@ -175,11 +243,9 @@ class MainActivity : LoadingActivity() { val tranY: Float = Resolution.convertDpToPixel(120F, App.getContext()) val time = 200L if (show) { - viewBinding.fab.animate().translationY(0f).alpha(1f).setDuration(time) - .start() + viewBinding.fab.animate().translationY(0f).alpha(1f).setDuration(time).start() } else { - viewBinding.fab.animate().translationY(tranY).alpha(0f).setDuration(time) - .start() + viewBinding.fab.animate().translationY(tranY).alpha(0f).setDuration(time).start() } } catch (e: Exception) { Log.e(TAG, "Error in showFloatButton: ${e.message}") @@ -204,7 +270,8 @@ class MainActivity : LoadingActivity() { private fun updateUserRemark(userId: Int) { try { - var remark = AppManager.mRemarkSharedPreferences.getString("Remark$userId", "User $userId") + var remark = + AppManager.mRemarkSharedPreferences.getString("Remark$userId", "User $userId") if (remark.isNullOrEmpty()) { remark = "User $userId" } @@ -217,21 +284,21 @@ class MainActivity : LoadingActivity() { } private val apkPathResult = - registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { - try { - if (it.resultCode == RESULT_OK) { - it.data?.let { data -> - val userId = data.getIntExtra("userID", 0) - val source = data.getStringExtra("source") - if (source != null) { - fragmentList[userId].installApk(source) + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { + try { + if (it.resultCode == RESULT_OK) { + it.data?.let { data -> + val userId = data.getIntExtra("userID", 0) + val source = data.getStringExtra("source") + if (source != null) { + fragmentList[userId].installApk(source) + } } } + } catch (e: Exception) { + Log.e(TAG, "Error handling APK path result: ${e.message}") } - } catch (e: Exception) { - Log.e(TAG, "Error handling APK path result: ${e.message}") } - } override fun onCreateOptionsMenu(menu: Menu?): Boolean { try { @@ -248,20 +315,19 @@ class MainActivity : LoadingActivity() { when (item.itemId) { R.id.main_git -> { val intent = - Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/ALEX5402/NewBlackbox")) + Intent( + Intent.ACTION_VIEW, + Uri.parse("https://github.com/ALEX5402/NewBlackbox") + ) startActivity(intent) } - R.id.main_setting -> { SettingActivity.start(this) } - R.id.main_tg -> { - val intent = - Intent(Intent.ACTION_VIEW, Uri.parse("https://t.me/newblackboxa")) + val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://t.me/newblackboxa")) startActivity(intent) } - R.id.fake_location -> { // toast("Still Developing") val intent = Intent(this, FakeManagerActivity::class.java) diff --git a/black-reflection/.gitignore b/black-reflection/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/black-reflection/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/black-reflection/build.gradle.kts b/black-reflection/build.gradle.kts new file mode 100644 index 0000000..954f3f0 --- /dev/null +++ b/black-reflection/build.gradle.kts @@ -0,0 +1,7 @@ +plugins { + id("java-library") +} +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 +} diff --git a/black-reflection/src/main/java/top/niunaijun/blackreflection/BlackNullPointerException.java b/black-reflection/src/main/java/top/niunaijun/blackreflection/BlackNullPointerException.java new file mode 100644 index 0000000..4a3ec9e --- /dev/null +++ b/black-reflection/src/main/java/top/niunaijun/blackreflection/BlackNullPointerException.java @@ -0,0 +1,15 @@ +package top.niunaijun.blackreflection; + +/** + * on 2022/2/16. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class BlackNullPointerException extends NullPointerException { + public BlackNullPointerException(String s) { + super(s); + } +} diff --git a/black-reflection/src/main/java/top/niunaijun/blackreflection/BlackReflection.java b/black-reflection/src/main/java/top/niunaijun/blackreflection/BlackReflection.java new file mode 100644 index 0000000..51b443b --- /dev/null +++ b/black-reflection/src/main/java/top/niunaijun/blackreflection/BlackReflection.java @@ -0,0 +1,245 @@ +package top.niunaijun.blackreflection; + +import java.lang.annotation.Annotation; +import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; + +import top.niunaijun.blackreflection.annotation.BClass; +import top.niunaijun.blackreflection.annotation.BClassName; +import top.niunaijun.blackreflection.annotation.BClassNameNotProcess; +import top.niunaijun.blackreflection.annotation.BConstructor; +import top.niunaijun.blackreflection.annotation.BConstructorNotProcess; +import top.niunaijun.blackreflection.annotation.BField; +import top.niunaijun.blackreflection.annotation.BFieldCheckNotProcess; +import top.niunaijun.blackreflection.annotation.BFieldNotProcess; +import top.niunaijun.blackreflection.annotation.BFieldSetNotProcess; +import top.niunaijun.blackreflection.annotation.BMethodCheckNotProcess; +import top.niunaijun.blackreflection.annotation.BParamClass; +import top.niunaijun.blackreflection.annotation.BParamClassName; +import top.niunaijun.blackreflection.utils.Reflector; + + +@SuppressWarnings("unchecked") +public class BlackReflection { + public static boolean DEBUG = false; + public static boolean CACHE = false; + private static final Map, Object> sProxyCache = new HashMap<>(); + private static final Map, Object> sProxyWithExceptionCache = new HashMap<>(); + + // key caller + private static final WeakHashMap, Object>> sCallerProxyCache = new WeakHashMap<>(); + +// public static T create(Class clazz, final Object caller) { +// return create(clazz, caller, false); +// } + + public static T create(Class clazz, final Object caller, boolean withException) { + try { + T proxy = getProxy(clazz, caller, withException); + if (proxy != null) + return proxy; + final WeakReference weakCaller = caller == null ? null : new WeakReference<>(caller); + + final Class aClass = getClassNameByBlackClass(clazz); + Object o = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + String name = method.getName(); + Class returnType = method.getReturnType(); + + try { + boolean isStatic = weakCaller == null; + + Object callerByWeak = isStatic ? null : weakCaller.get(); + + // fidel + BField bField = method.getAnnotation(BField.class); + BFieldNotProcess bFieldNotProcess = method.getAnnotation(BFieldNotProcess.class); + if (bField != null || bFieldNotProcess != null) { + Object call; + Reflector on = Reflector.on(aClass).field(name); + if (isStatic) { + call = on.get(); + } else { + if (callerByWeak == null) { + return generateNullValue(returnType); + } + call = on.get(callerByWeak); + } + return call; + } + + // void + BFieldSetNotProcess bFieldSetNotProcess = method.getAnnotation(BFieldSetNotProcess.class); + if (bFieldSetNotProcess != null) { + // startsWith "_set_" + name = name.substring("_set_".length()); + Reflector on = Reflector.on(aClass).field(name); + if (isStatic) { + on.set(args[0]); + } else { + if (callerByWeak == null) { + return 0; + } + on.set(callerByWeak, args[0]); + } + return 0; + } + + // check field + BFieldCheckNotProcess bFieldCheckNotProcess = method.getAnnotation(BFieldCheckNotProcess.class); + if (bFieldCheckNotProcess != null) { + // startsWith "_check_" + name = name.substring("_check_".length()); + try { + Reflector on = Reflector.on(aClass).field(name); + return on.getField(); + } catch (Throwable ignored) { + return null; + } + } + + Class[] paramClass = getParamClass(method); + + // check method + BMethodCheckNotProcess bMethodCheckNotProcess = method.getAnnotation(BMethodCheckNotProcess.class); + if (bMethodCheckNotProcess != null) { + // startsWith "_check_" + name = name.substring("_check_".length()); + try { + return Reflector.on(aClass).method(name, paramClass).getMethod(); + } catch (Throwable ignored) { + return null; + } + } + + // method + BConstructor bConstructor = method.getAnnotation(BConstructor.class); + BConstructorNotProcess bConstructorNotProcess = method.getAnnotation(BConstructorNotProcess.class); + if (bConstructor != null || bConstructorNotProcess != null) { + Reflector on = Reflector.on(aClass).constructor(paramClass); + return on.newInstance(args); + } + + Object call; + Reflector on = Reflector.on(aClass).method(name, paramClass); + if (isStatic) { + call = on.call(args); + } else { + if (callerByWeak == null) { + return generateNullValue(returnType); + } + call = on.callByCaller(callerByWeak, args); + } + return call; + } catch (Throwable throwable) { + if (DEBUG) { + if (throwable.getCause() != null) { + throwable.getCause().printStackTrace(); + } else { + throwable.printStackTrace(); + } + } + if (throwable instanceof BlackNullPointerException) { + throw new NullPointerException(throwable.getMessage()); + } + if (withException) { + throw throwable; + } + } + + return generateNullValue(returnType); + } + }); + + if (caller == null) { + if (withException) { + sProxyWithExceptionCache.put(clazz, o); + } else { + sProxyCache.put(clazz, o); + } + } + return (T) o; + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + return null; + } + + private static T getProxy(Class clazz, final Object caller, boolean withException) { + try { + if (caller == null) { + Object o; + if (withException) { + o = sProxyWithExceptionCache.get(clazz); + } else { + o = sProxyCache.get(clazz); + } + if (o != null) { + return (T) o; + } + } + } catch (Throwable ignore) { + } + return null; + } + + private static Class[] getParamClass(Method method) throws Throwable { + Annotation[][] parameterAnnotations = method.getParameterAnnotations(); + Class[] parameterTypes = method.getParameterTypes(); + Class[] param = new Class[parameterTypes.length]; + + for (int i = 0; i < parameterTypes.length; i++) { + Annotation[] parameterAnnotation = parameterAnnotations[i]; + boolean found = false; + for (Annotation annotation : parameterAnnotation) { + if (annotation instanceof BParamClassName) { + found = true; + param[i] = Class.forName(((BParamClassName) annotation).value()); + break; + } else if (annotation instanceof BParamClass) { + found = true; + param[i] = ((BParamClass) annotation).value(); + break; + } + } + + if (!found) { + param[i] = parameterTypes[i]; + } + } + return param; + } + + private static Class getClassNameByBlackClass(Class clazz) throws ClassNotFoundException { + BClass bClass = clazz.getAnnotation(BClass.class); + BClassName bClassName = clazz.getAnnotation(BClassName.class); + BClassNameNotProcess bClassNameNotProcess = clazz.getAnnotation(BClassNameNotProcess.class); + if (bClass == null && bClassName == null && bClassNameNotProcess == null) { + throw new RuntimeException("Not found @BlackClass or @BlackStrClass"); + } + + if (bClass != null) { + return bClass.value(); + } else if (bClassName != null) { + return Class.forName(bClassName.value()); + } else { + return Class.forName(bClassNameNotProcess.value()); + } + } + + private static Object generateNullValue(Class returnType) { + if (returnType == void.class) { + return 0; + } + if (returnType.isPrimitive()) { + throw new BlackNullPointerException("value is null!"); + } + return null; + } +} diff --git a/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BClass.java b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BClass.java new file mode 100644 index 0000000..bf10c7b --- /dev/null +++ b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BClass.java @@ -0,0 +1,14 @@ +package top.niunaijun.blackreflection.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + + +@Retention(RUNTIME) +@Target({TYPE}) +public @interface BClass { + Class value(); +} diff --git a/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BClassName.java b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BClassName.java new file mode 100644 index 0000000..3f7e4b4 --- /dev/null +++ b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BClassName.java @@ -0,0 +1,14 @@ +package top.niunaijun.blackreflection.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + + +@Retention(RUNTIME) +@Target({TYPE}) +public @interface BClassName { + String value(); +} diff --git a/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BClassNameNotProcess.java b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BClassNameNotProcess.java new file mode 100644 index 0000000..e2fe69c --- /dev/null +++ b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BClassNameNotProcess.java @@ -0,0 +1,14 @@ +package top.niunaijun.blackreflection.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + + +@Retention(RUNTIME) +@Target({TYPE}) +public @interface BClassNameNotProcess { + String value(); +} diff --git a/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BConstructor.java b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BConstructor.java new file mode 100644 index 0000000..297b7c3 --- /dev/null +++ b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BConstructor.java @@ -0,0 +1,21 @@ +package top.niunaijun.blackreflection.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * on 2022/2/16. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +@Retention(RUNTIME) +@Target({METHOD}) +public @interface BConstructor { + +} diff --git a/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BConstructorNotProcess.java b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BConstructorNotProcess.java new file mode 100644 index 0000000..c0e8873 --- /dev/null +++ b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BConstructorNotProcess.java @@ -0,0 +1,21 @@ +package top.niunaijun.blackreflection.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * on 2022/2/16. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +@Retention(RUNTIME) +@Target({METHOD}) +public @interface BConstructorNotProcess { + +} diff --git a/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BField.java b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BField.java new file mode 100644 index 0000000..c820055 --- /dev/null +++ b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BField.java @@ -0,0 +1,14 @@ +package top.niunaijun.blackreflection.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + + +@Retention(RUNTIME) +@Target({METHOD}) +public @interface BField { +} diff --git a/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BFieldCheckNotProcess.java b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BFieldCheckNotProcess.java new file mode 100644 index 0000000..59d2f2f --- /dev/null +++ b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BFieldCheckNotProcess.java @@ -0,0 +1,13 @@ +package top.niunaijun.blackreflection.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + + +@Retention(RUNTIME) +@Target({METHOD}) +public @interface BFieldCheckNotProcess { +} diff --git a/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BFieldNotProcess.java b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BFieldNotProcess.java new file mode 100644 index 0000000..1288060 --- /dev/null +++ b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BFieldNotProcess.java @@ -0,0 +1,15 @@ +package top.niunaijun.blackreflection.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + + +@Retention(RUNTIME) +@Target({METHOD}) +public @interface BFieldNotProcess { +} diff --git a/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BFieldSetNotProcess.java b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BFieldSetNotProcess.java new file mode 100644 index 0000000..70e672a --- /dev/null +++ b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BFieldSetNotProcess.java @@ -0,0 +1,13 @@ +package top.niunaijun.blackreflection.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + + +@Retention(RUNTIME) +@Target({METHOD}) +public @interface BFieldSetNotProcess { +} diff --git a/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BMethod.java b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BMethod.java new file mode 100644 index 0000000..773774c --- /dev/null +++ b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BMethod.java @@ -0,0 +1,13 @@ +package top.niunaijun.blackreflection.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + + +@Retention(RUNTIME) +@Target({METHOD}) +public @interface BMethod { +} diff --git a/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BMethodCheckNotProcess.java b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BMethodCheckNotProcess.java new file mode 100644 index 0000000..2e440f8 --- /dev/null +++ b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BMethodCheckNotProcess.java @@ -0,0 +1,13 @@ +package top.niunaijun.blackreflection.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + + +@Retention(RUNTIME) +@Target({METHOD}) +public @interface BMethodCheckNotProcess { +} diff --git a/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BParamClass.java b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BParamClass.java new file mode 100644 index 0000000..f6fd4c2 --- /dev/null +++ b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BParamClass.java @@ -0,0 +1,15 @@ +package top.niunaijun.blackreflection.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + + +@Retention(RUNTIME) +@Target({PARAMETER}) +public @interface BParamClass { + Class value(); +} diff --git a/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BParamClassName.java b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BParamClassName.java new file mode 100644 index 0000000..1f0bf45 --- /dev/null +++ b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BParamClassName.java @@ -0,0 +1,14 @@ +package top.niunaijun.blackreflection.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + + +@Retention(RUNTIME) +@Target({PARAMETER}) +public @interface BParamClassName { + String value(); +} diff --git a/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BStaticField.java b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BStaticField.java new file mode 100644 index 0000000..83ccacb --- /dev/null +++ b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BStaticField.java @@ -0,0 +1,13 @@ +package top.niunaijun.blackreflection.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + + +@Retention(RUNTIME) +@Target({METHOD}) +public @interface BStaticField { +} diff --git a/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BStaticMethod.java b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BStaticMethod.java new file mode 100644 index 0000000..d36493d --- /dev/null +++ b/black-reflection/src/main/java/top/niunaijun/blackreflection/annotation/BStaticMethod.java @@ -0,0 +1,13 @@ +package top.niunaijun.blackreflection.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + + +@Retention(RUNTIME) +@Target({METHOD}) +public @interface BStaticMethod { +} diff --git a/black-reflection/src/main/java/top/niunaijun/blackreflection/utils/ClassUtil.java b/black-reflection/src/main/java/top/niunaijun/blackreflection/utils/ClassUtil.java new file mode 100644 index 0000000..968b1e6 --- /dev/null +++ b/black-reflection/src/main/java/top/niunaijun/blackreflection/utils/ClassUtil.java @@ -0,0 +1,39 @@ +package top.niunaijun.blackreflection.utils; + +import top.niunaijun.blackreflection.annotation.BClass; +import top.niunaijun.blackreflection.annotation.BClassName; +import top.niunaijun.blackreflection.annotation.BClassNameNotProcess; + +/** + * on 2022/2/18. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ClassUtil { + public static Class classReady(Class clazz) { + BClassNameNotProcess bClassNameNotProcess = clazz.getAnnotation(BClassNameNotProcess.class); + if (bClassNameNotProcess != null) { + return classReady(bClassNameNotProcess.value()); + } + BClass annotation = clazz.getAnnotation(BClass.class); + if (annotation != null) { + return annotation.value(); + } + BClassName bClassName = clazz.getAnnotation(BClassName.class); + if (bClassName != null) { + return classReady(bClassName.value()); + } + return null; + } + + private static Class classReady(String clazz) { + try { + return Class.forName(clazz); + } catch (ClassNotFoundException ignored) { + } + return null; + } +} diff --git a/black-reflection/src/main/java/top/niunaijun/blackreflection/utils/Reflector.java b/black-reflection/src/main/java/top/niunaijun/blackreflection/utils/Reflector.java new file mode 100644 index 0000000..88e3a00 --- /dev/null +++ b/black-reflection/src/main/java/top/niunaijun/blackreflection/utils/Reflector.java @@ -0,0 +1,440 @@ +package top.niunaijun.blackreflection.utils; + + + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +public class Reflector { + public static final String LOG_TAG ="Reflector"; + + protected Class mType; + protected Object mCaller; + protected Constructor mConstructor; + protected Field mField; + protected Method mMethod; + + + public static Reflector on(String name) throws Exception { + return on(name, true, Reflector.class.getClassLoader()); + } + + public static Reflector on(String name, boolean initialize) throws Exception { + return on(name, initialize, Reflector.class.getClassLoader()); + } + + public static Reflector on(String name, boolean initialize, ClassLoader loader) throws Exception { + try { + return on(Class.forName(name, initialize, loader)); + } catch (Throwable e) { + throw new Exception("Oops!", e); + } + } + + public static Reflector on(Class type) { + Reflector reflector = new Reflector(); + reflector.mType = type; + return reflector; + } + + public static Reflector with(Object caller) throws Exception { + return on(caller.getClass()).bind(caller); + } + + protected Reflector() { + + } + + public Reflector constructor(Class... parameterTypes) throws Exception { + try { + mConstructor = mType.getDeclaredConstructor(parameterTypes); + mConstructor.setAccessible(true); + mField = null; + mMethod = null; + return this; + } catch (Throwable e) { + throw new Exception("Oops!", e); + } + } + + @SuppressWarnings("unchecked") + public R newInstance(Object... initargs) throws Exception { + if (mConstructor == null) { + throw new Exception("Constructor was null!"); + } + try { + return (R) mConstructor.newInstance(initargs); + } catch (InvocationTargetException e) { + throw new Exception("Oops!", e.getTargetException()); + } catch (Throwable e) { + throw new Exception("Oops!", e); + } + } + + protected Object checked(Object caller) throws Exception { + if (caller == null || mType.isInstance(caller)) { + return caller; + } + throw new Exception("Caller [" + caller + "] is not a instance of type [" + mType + "]!"); + } + + protected void check(Object caller, Member member, String name) throws Exception { + if (member == null) { + throw new Exception(name + " was null!"); + } + if (caller == null && !Modifier.isStatic(member.getModifiers())) { + throw new Exception("Need a caller!"); + } + checked(caller); + } + + public Reflector bind(Object caller) throws Exception { + mCaller = checked(caller); + return this; + } + + public Reflector unbind() { + mCaller = null; + return this; + } + + public Reflector field(String name) throws Exception { + try { + mField = findField(name); + mField.setAccessible(true); + mConstructor = null; + mMethod = null; + return this; + } catch (Throwable e) { + throw new Exception("Oops!", e); + } + } + + public Field getField() { + return mField; + } + + protected Field findField(String name) throws NoSuchFieldException { + try { + return mType.getField(name); + } catch (NoSuchFieldException e) { + for (Class cls = mType; cls != null; cls = cls.getSuperclass()) { + try { + return cls.getDeclaredField(name); + } catch (NoSuchFieldException ex) { + // Ignored + } + } + throw e; + } + } + + @SuppressWarnings("unchecked") + public R get() throws Exception { + return get(mCaller); + } + + @SuppressWarnings("unchecked") + public R get(Object caller) throws Exception { + check(caller, mField, "Field"); + try { + return (R) mField.get(caller); + } catch (Throwable e) { + throw new Exception("Oops!", e); + } + } + + public Reflector set(Object value) throws Exception { + return set(mCaller, value); + } + + public Reflector set(Object caller, Object value) throws Exception { + check(caller, mField, "Field"); + try { + mField.set(caller, value); + return this; + } catch (Throwable e) { + throw new Exception("Oops!", e); + } + } + + public Reflector method(String name, Class... parameterTypes) throws Exception { + try { + mMethod = findMethod(name, parameterTypes); + mMethod.setAccessible(true); + mConstructor = null; + mField = null; + return this; + } catch (NoSuchMethodException e) { + throw new Exception("Oops!", e); + } + } + + public Method getMethod() { + return mMethod; + } + + protected Method findMethod(String name, Class... parameterTypes) throws NoSuchMethodException { + try { + return mType.getMethod(name, parameterTypes); + } catch (NoSuchMethodException e) { + for (Class cls = mType; cls != null; cls = cls.getSuperclass()) { + try { + return cls.getDeclaredMethod(name, parameterTypes); + } catch (NoSuchMethodException ex) { + // Ignored + } + } + throw e; + } + } + + public R call(Object... args) throws Exception { + return callByCaller(mCaller, args); + } + + @SuppressWarnings("unchecked") + public R callByCaller(Object caller, Object... args) throws Exception { + check(caller, mMethod, "Method"); + try { + return (R) mMethod.invoke(caller, args); + } catch (InvocationTargetException e) { + throw new Exception("Oops!", e.getTargetException()); + } catch (Throwable e) { + throw new Exception("Oops!", e); + } + } + + public static class QuietReflector extends Reflector { + + protected Throwable mIgnored; + + public static QuietReflector on(String name) { + return on(name, true, QuietReflector.class.getClassLoader()); + } + + public static QuietReflector on(String name, boolean initialize) { + return on(name, initialize, QuietReflector.class.getClassLoader()); + } + + public static QuietReflector on(String name, boolean initialize, ClassLoader loader) { + Class cls = null; + try { + cls = Class.forName(name, initialize, loader); + return on(cls, null); + } catch (Throwable e) { +// Log.w(LOG_TAG, "Oops!", e); + return on(cls, e); + } + } + + public static QuietReflector on(Class type) { + return on(type, (type == null) ? new Exception("Type was null!") : null); + } + + private static QuietReflector on(Class type, Throwable ignored) { + QuietReflector reflector = new QuietReflector(); + reflector.mType = type; + reflector.mIgnored = ignored; + return reflector; + } + + public static QuietReflector with(Object caller) { + if (caller == null) { + return on((Class) null); + } + return on(caller.getClass()).bind(caller); + } + + protected QuietReflector() { + + } + + public Throwable getIgnored() { + return mIgnored; + } + + protected boolean skip() { + return skipAlways() || mIgnored != null; + } + + protected boolean skipAlways() { + return mType == null; + } + + @Override + public QuietReflector constructor(Class... parameterTypes) { + if (skipAlways()) { + return this; + } + try { + mIgnored = null; + super.constructor(parameterTypes); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return this; + } + + @Override + public R newInstance(Object... initargs) { + if (skip()) { + return null; + } + try { + mIgnored = null; + return super.newInstance(initargs); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return null; + } + + @Override + public QuietReflector bind(Object obj) { + if (skipAlways()) { + return this; + } + try { + mIgnored = null; + super.bind(obj); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return this; + } + + @Override + public QuietReflector unbind() { + super.unbind(); + return this; + } + + @Override + public QuietReflector field(String name) { + if (skipAlways()) { + return this; + } + try { + mIgnored = null; + super.field(name); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return this; + } + + @Override + public R get() { + if (skip()) { + return null; + } + try { + mIgnored = null; + return super.get(); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return null; + } + + @Override + public R get(Object caller) { + if (skip()) { + return null; + } + try { + mIgnored = null; + return super.get(caller); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return null; + } + + @Override + public QuietReflector set(Object value) { + if (skip()) { + return this; + } + try { + mIgnored = null; + super.set(value); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return this; + } + + @Override + public QuietReflector set(Object caller, Object value) { + if (skip()) { + return this; + } + try { + mIgnored = null; + super.set(caller, value); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return this; + } + + @Override + public QuietReflector method(String name, Class... parameterTypes) { + if (skipAlways()) { + return this; + } + try { + mIgnored = null; + super.method(name, parameterTypes); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return this; + } + + @Override + public R call(Object... args) { + if (skip()) { + return null; + } + try { + mIgnored = null; + return super.call(args); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return null; + } + + @Override + public R callByCaller(Object caller, Object... args) { + if (skip()) { + return null; + } + try { + mIgnored = null; + return super.callByCaller(caller, args); + } catch (Throwable e) { + mIgnored = e; +// Log.w(LOG_TAG, "Oops!", e); + } + return null; + } + } +} diff --git a/build.gradle b/build.gradle index 8418974..acffbae 100644 --- a/build.gradle +++ b/build.gradle @@ -10,14 +10,7 @@ ext { targetSdkVersion = 34 minSdk = 21 versionCode = 1 - versionName = "3.0.7r4" - - - - - - + versionName = "3.0.9-beta2" xVersion = '1.1.0' - blackReflection = '1.1.2' hiddenApiBypass = '4.3' } \ No newline at end of file diff --git a/compiler/.gitignore b/compiler/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/compiler/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/compiler/build.gradle.kts b/compiler/build.gradle.kts new file mode 100644 index 0000000..8b468a2 --- /dev/null +++ b/compiler/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + id("java-library") +} +java { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 +} + +dependencies { + implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar")))) + annotationProcessor("com.google.auto.service:auto-service:1.1.1") + compileOnly("com.google.auto.service:auto-service:1.1.1") + implementation("com.squareup:javapoet:1.13.0") + implementation(project(":black-reflection")) +} diff --git a/compiler/src/main/java/top/niunaijun/blackreflection/BlackReflectionInfo.java b/compiler/src/main/java/top/niunaijun/blackreflection/BlackReflectionInfo.java new file mode 100644 index 0000000..89758a7 --- /dev/null +++ b/compiler/src/main/java/top/niunaijun/blackreflection/BlackReflectionInfo.java @@ -0,0 +1,25 @@ +package top.niunaijun.blackreflection; + + +public class BlackReflectionInfo { + // 反射指向的类 + private String realClass; + // 当前注解的className + private String className; + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + public String getRealClass() { + return realClass; + } + + public void setRealClass(String realClass) { + this.realClass = realClass; + } +} diff --git a/compiler/src/main/java/top/niunaijun/blackreflection/BlackReflectionInterfaceInfo.java b/compiler/src/main/java/top/niunaijun/blackreflection/BlackReflectionInterfaceInfo.java new file mode 100644 index 0000000..fa0b1ef --- /dev/null +++ b/compiler/src/main/java/top/niunaijun/blackreflection/BlackReflectionInterfaceInfo.java @@ -0,0 +1,27 @@ +package top.niunaijun.blackreflection; + +import javax.lang.model.element.ExecutableElement; + + +public class BlackReflectionInterfaceInfo { + // 方法element + private ExecutableElement executableElement; + // 是否是字段 + private boolean isField; + + public boolean isField() { + return isField; + } + + public void setField(boolean field) { + isField = field; + } + + public ExecutableElement getExecutableElement() { + return executableElement; + } + + public void setExecutableElement(ExecutableElement executableElement) { + this.executableElement = executableElement; + } +} diff --git a/compiler/src/main/java/top/niunaijun/blackreflection/BlackReflectionProcessor.java b/compiler/src/main/java/top/niunaijun/blackreflection/BlackReflectionProcessor.java new file mode 100644 index 0000000..52e2e86 --- /dev/null +++ b/compiler/src/main/java/top/niunaijun/blackreflection/BlackReflectionProcessor.java @@ -0,0 +1,190 @@ +package top.niunaijun.blackreflection; + +import com.google.auto.service.AutoService; + +import java.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Filer; +import javax.annotation.processing.Messager; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.Processor; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.MirroredTypeException; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; + +import top.niunaijun.blackreflection.annotation.BClass; +import top.niunaijun.blackreflection.annotation.BConstructor; +import top.niunaijun.blackreflection.annotation.BField; +import top.niunaijun.blackreflection.annotation.BMethod; +import top.niunaijun.blackreflection.annotation.BStaticField; +import top.niunaijun.blackreflection.annotation.BStaticMethod; +import top.niunaijun.blackreflection.annotation.BClassName; +import top.niunaijun.blackreflection.proxy.BlackReflectionInterfaceProxy; +import top.niunaijun.blackreflection.proxy.BlackReflectionProxy; + + +@AutoService(Processor.class) +public class BlackReflectionProcessor extends AbstractProcessor { + + private Map mBlackReflectionProxies; + private Map mBlackReflectionInterfaceProxies; + private Map mRealMaps = new HashMap<>(); + + private Messager mMessager; + private Elements mElementUtils; //元素相关的辅助类 + private Filer mFiler; + + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + mMessager = processingEnv.getMessager(); + mElementUtils = processingEnv.getElementUtils(); + mFiler = processingEnv.getFiler(); + mBlackReflectionProxies = new Hashtable<>(); + mBlackReflectionInterfaceProxies = new Hashtable<>(); + mRealMaps = new Hashtable<>(); + } + + @Override + public Set getSupportedAnnotationTypes() { + HashSet supportTypes = new LinkedHashSet<>(); + supportTypes.add(BClass.class.getCanonicalName()); + supportTypes.add(BClassName.class.getCanonicalName()); + + supportTypes.add(BField.class.getCanonicalName()); + supportTypes.add(BStaticField.class.getCanonicalName()); + + supportTypes.add(BMethod.class.getCanonicalName()); + supportTypes.add(BStaticMethod.class.getCanonicalName()); + + supportTypes.add(BConstructor.class.getCanonicalName()); + return supportTypes; + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.RELEASE_8; + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + mBlackReflectionProxies.clear(); + mBlackReflectionInterfaceProxies.clear(); + mRealMaps.clear(); + + for (Element element : roundEnv.getElementsAnnotatedWith(BClassName.class)) { + BClassName annotation = element.getAnnotation(BClassName.class); + doProcess(element, annotation.value()); + } + + for (Element element : roundEnv.getElementsAnnotatedWith(BClass.class)) { + BClass annotation = element.getAnnotation(BClass.class); + String aClass = getClass(annotation).toString(); + doProcess(element, aClass); + } + + for (Element element : roundEnv.getElementsAnnotatedWith(BStaticMethod.class)) { + doInterfaceProcess(element, true, false); + } + for (Element element : roundEnv.getElementsAnnotatedWith(BMethod.class)) { + doInterfaceProcess(element, false, false); + } + for (Element element : roundEnv.getElementsAnnotatedWith(BStaticField.class)) { + doInterfaceProcess(element, true, true); + } + for (Element element : roundEnv.getElementsAnnotatedWith(BField.class)) { + doInterfaceProcess(element, false, true); + } + for (Element element : roundEnv.getElementsAnnotatedWith(BConstructor.class)) { + doInterfaceProcess(element, true, false); + } + + for (BlackReflectionInterfaceProxy value : mBlackReflectionInterfaceProxies.values()) { + try { + value.setRealMap(mRealMaps); + value.generateInterfaceCode().writeTo(mFiler); + } catch (IOException e) { + e.printStackTrace(); + } + } + for (BlackReflectionProxy value : mBlackReflectionProxies.values()) { + try { + value.generateJavaCode().writeTo(mFiler); + } catch (IOException e) { + e.printStackTrace(); + } + } + return true; + } + + private void doInterfaceProcess(Element element, boolean isStatic, boolean isField) { + String className = element.getEnclosingElement().asType().toString(); + ExecutableElement executableElement = (ExecutableElement) element; + String packageName = mElementUtils.getPackageOf(executableElement).getQualifiedName().toString(); + + BlackReflectionInterfaceInfo interfaceInfo = new BlackReflectionInterfaceInfo(); + interfaceInfo.setExecutableElement(executableElement); + interfaceInfo.setField(isField); + + BlackReflectionInterfaceProxy reflectionInterfaceProxy = getReflectionInterfaceProxy(packageName, + className + (isStatic ? "Static" : "Context"), + className); + reflectionInterfaceProxy.add(interfaceInfo); + } + + private void doProcess(Element element, String realClassName) { + String packageName = mElementUtils.getPackageOf(element).getQualifiedName().toString(); + String className = element.asType().toString(); + BlackReflectionInfo info = new BlackReflectionInfo(); + info.setRealClass(realClassName); + info.setClassName(className); + + getReflectionProxy(packageName, className, info); + + // 创建两个基本类 + getReflectionInterfaceProxy(packageName, className + "Context", + className); + getReflectionInterfaceProxy(packageName, className + "Static", + className); + mRealMaps.put(className, realClassName); + } + + private static TypeMirror getClass(BClass annotation) { + try { + annotation.value(); // this should throw + } catch (MirroredTypeException mte) { + return mte.getTypeMirror(); + } + return null; // can this ever happen ?? + } + + public BlackReflectionProxy getReflectionProxy(String packageName, String className, BlackReflectionInfo info) { + BlackReflectionProxy blackReflectionProxy = mBlackReflectionProxies.get(className); + if (blackReflectionProxy == null) { + blackReflectionProxy = new BlackReflectionProxy(packageName, info); + mBlackReflectionProxies.put(className, blackReflectionProxy); + } + return blackReflectionProxy; + } + + public BlackReflectionInterfaceProxy getReflectionInterfaceProxy(String packageName, String className, String origClassName) { + BlackReflectionInterfaceProxy blackReflectionProxy = mBlackReflectionInterfaceProxies.get(className); + if (blackReflectionProxy == null) { + blackReflectionProxy = new BlackReflectionInterfaceProxy(packageName, className, origClassName); + mBlackReflectionInterfaceProxies.put(className, blackReflectionProxy); + } + return blackReflectionProxy; + } +} diff --git a/compiler/src/main/java/top/niunaijun/blackreflection/proxy/BlackReflectionInterfaceProxy.java b/compiler/src/main/java/top/niunaijun/blackreflection/proxy/BlackReflectionInterfaceProxy.java new file mode 100644 index 0000000..5ee5651 --- /dev/null +++ b/compiler/src/main/java/top/niunaijun/blackreflection/proxy/BlackReflectionInterfaceProxy.java @@ -0,0 +1,202 @@ +package top.niunaijun.blackreflection.proxy; + +import com.squareup.javapoet.AnnotationSpec; +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.JavaFile; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.ParameterSpec; +import com.squareup.javapoet.TypeName; +import com.squareup.javapoet.TypeSpec; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.lang.model.element.Modifier; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.MirroredTypeException; +import javax.lang.model.type.TypeMirror; + +import top.niunaijun.blackreflection.BlackReflectionInterfaceInfo; +import top.niunaijun.blackreflection.annotation.BConstructor; +import top.niunaijun.blackreflection.annotation.BConstructorNotProcess; +import top.niunaijun.blackreflection.annotation.BFieldCheckNotProcess; +import top.niunaijun.blackreflection.annotation.BFieldNotProcess; +import top.niunaijun.blackreflection.annotation.BFieldSetNotProcess; +import top.niunaijun.blackreflection.annotation.BMethodCheckNotProcess; +import top.niunaijun.blackreflection.annotation.BParamClass; +import top.niunaijun.blackreflection.annotation.BClassNameNotProcess; +import top.niunaijun.blackreflection.annotation.BParamClassName; + +/** + * Created by sunwanquan on 2020/1/8. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class BlackReflectionInterfaceProxy { + + private final List mReflections = new ArrayList<>(); + // fake.android.app.ActivityThreadStatic or ActivityThreadContext + private final String mClassName; + // fake.android.app + private final String mPackageName; + // fake.android.app.ActivityThread + private final String mOrigClassName; + private Map realMaps; + + public BlackReflectionInterfaceProxy(String packageName, String className, String origClassName) { + mPackageName = packageName; + mClassName = className; + mOrigClassName = origClassName; + } + + public JavaFile generateInterfaceCode() { + String finalClass = mClassName + .replace(mPackageName + ".", "") + .replace(".", ""); + AnnotationSpec annotationSpec = AnnotationSpec.builder(BClassNameNotProcess.class) + .addMember("value","$S", realMaps.get(mOrigClassName)) + .build(); + + // generaClass + TypeSpec.Builder interfaceBuilder = TypeSpec.interfaceBuilder(finalClass) + .addAnnotation(annotationSpec) + .addModifiers(Modifier.PUBLIC); + + for (BlackReflectionInterfaceInfo reflection : mReflections) { + MethodSpec.Builder method = MethodSpec.methodBuilder(reflection.getExecutableElement().getSimpleName().toString()) + .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT); + + List parameterSpecs = new ArrayList<>(); + for (VariableElement typeParameter : reflection.getExecutableElement().getParameters()) { + ParameterSpec.Builder builder = ParameterSpec.builder(ClassName.get(typeParameter.asType()), typeParameter.getSimpleName().toString()); + if (typeParameter.getAnnotation(BParamClassName.class) != null) { + BParamClassName annotation = typeParameter.getAnnotation(BParamClassName.class); + builder.addAnnotation(AnnotationSpec.get(annotation)); + } + if (typeParameter.getAnnotation(BParamClass.class) != null) { + BParamClass annotation = typeParameter.getAnnotation(BParamClass.class); + String annotationValue = getClass(annotation).toString(); + Class aClass = parseBaseClass(annotationValue); + if (aClass != null) { + builder.addAnnotation(AnnotationSpec.builder(BParamClass.class) + .addMember("value", "$T.class", aClass).build()); + } else { + builder.addAnnotation(AnnotationSpec.builder(BParamClass.class) + .addMember("value", annotationValue + ".class").build()); + } + } + + ParameterSpec build = builder.build(); + parameterSpecs.add(build); + method.addParameter(build); + } + TypeName typeName = TypeName.get(reflection.getExecutableElement().getReturnType()); + method.returns(typeName.box()); + if (reflection.isField()) { + method.addAnnotation(AnnotationSpec.builder(BFieldNotProcess.class).build()); + // set field + interfaceBuilder.addMethod(generateFieldSet(reflection)); + // check field + interfaceBuilder.addMethod(generateFieldCheck(reflection)); + } else { + BConstructor annotation = reflection.getExecutableElement().getAnnotation(BConstructor.class); + if (annotation != null) { + method.addAnnotation(AnnotationSpec.builder(BConstructorNotProcess.class).build()); + } else { + // check method + interfaceBuilder.addMethod(generateMethodCheck(reflection, parameterSpecs)); + } + } + interfaceBuilder.addMethod(method.build()); + } + return JavaFile.builder(mPackageName, interfaceBuilder.build()).build(); + } + + private MethodSpec generateFieldSet(BlackReflectionInterfaceInfo reflection) { + MethodSpec.Builder method = MethodSpec.methodBuilder("_set_" + reflection.getExecutableElement().getSimpleName().toString()) + .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) + .addParameter(ClassName.get("java.lang", "Object"), "value", Modifier.FINAL) + .addAnnotation(AnnotationSpec.builder(BFieldSetNotProcess.class).build()); + return method.build(); + } + + private MethodSpec generateFieldCheck(BlackReflectionInterfaceInfo reflection) { + MethodSpec.Builder method = MethodSpec.methodBuilder("_check_" + reflection.getExecutableElement().getSimpleName().toString()) + .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) + .addAnnotation(AnnotationSpec.builder(BFieldCheckNotProcess.class).build()) + .returns(Field.class); + return method.build(); + } + + private MethodSpec generateMethodCheck(BlackReflectionInterfaceInfo reflection, List parameterSpecs) { + MethodSpec.Builder method = MethodSpec.methodBuilder("_check_" + reflection.getExecutableElement().getSimpleName().toString()) + .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT) + .addAnnotation(AnnotationSpec.builder(BMethodCheckNotProcess.class).build()) + .returns(Method.class); + for (ParameterSpec parameterSpec : parameterSpecs) { + method.addParameter(parameterSpec); + } + return method.build(); + } + + public void add(BlackReflectionInterfaceInfo interfaceInfo) { + mReflections.add(interfaceInfo); + } + + public void setRealMap(Map realMaps) { + this.realMaps = realMaps; + } + + private static TypeMirror getClass(BParamClass annotation) { + try { + annotation.value(); // this should throw + } catch (MirroredTypeException mte) { + return mte.getTypeMirror(); + } + return null; // can this ever happen ?? + } + + private static Class parseBaseClass(String className) { + switch (className) { + case "int": + return int.class; + case "byte": + return byte.class; + case "short": + return short.class; + case "long": + return long.class; + case "float": + return float.class; + case "double": + return double.class; + case "boolean": + return boolean.class; + case "char": + return char.class; + case "int[]": + return int[].class; + case "byte[]": + return byte[].class; + case "short[]": + return short[].class; + case "long[]": + return long[].class; + case "float[]": + return float[].class; + case "double[]": + return double[].class; + case "boolean[]": + return boolean[].class; + case "char[]": + return char[].class; + } + return null; + } +} diff --git a/compiler/src/main/java/top/niunaijun/blackreflection/proxy/BlackReflectionProxy.java b/compiler/src/main/java/top/niunaijun/blackreflection/proxy/BlackReflectionProxy.java new file mode 100644 index 0000000..a907833 --- /dev/null +++ b/compiler/src/main/java/top/niunaijun/blackreflection/proxy/BlackReflectionProxy.java @@ -0,0 +1,97 @@ +package top.niunaijun.blackreflection.proxy; + +import com.squareup.javapoet.ClassName; +import com.squareup.javapoet.JavaFile; +import com.squareup.javapoet.MethodSpec; +import com.squareup.javapoet.TypeSpec; + +import javax.lang.model.element.Modifier; + +import top.niunaijun.blackreflection.BlackReflectionInfo; +import top.niunaijun.blackreflection.utils.ClassUtils; + +/** + * Created by sunwanquan on 2020/1/8. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class BlackReflectionProxy { + + private static final ClassName BR = ClassName.get("top.niunaijun.blackreflection", "BlackReflection"); + private final BlackReflectionInfo mReflection; + + private final ClassName mContextInterface; + private final ClassName mStaticInterface; + private final String mPackageName; + + public BlackReflectionProxy(String packageName, BlackReflectionInfo reflection) { + mReflection = reflection; + mPackageName = packageName; + String finalClass = mReflection.getClassName() + .replace(packageName + ".", "") + .replace(".", ""); + + mContextInterface = ClassName.get(ClassUtils.getPackage(finalClass), ClassUtils.getName(finalClass + "Context")); + mStaticInterface = ClassName.get(ClassUtils.getPackage(finalClass), ClassUtils.getName(finalClass + "Static")); + } + + public JavaFile generateJavaCode() { + String finalClass = "BR" + mReflection.getClassName() + .replace(mPackageName + ".", "") + .replace(".", ""); + + // generaClass + TypeSpec reflection = TypeSpec.classBuilder(finalClass) + .addModifiers(Modifier.PUBLIC) + .addMethod(generaNotCallerMethod(true)) + .addMethod(generaNotCallerMethod(false)) + .addMethod(generaCallerMethod(true)) + .addMethod(generaCallerMethod(false)) + .addMethod(generaIsLoadMethod()) + .build(); + return JavaFile.builder(mPackageName, reflection).build(); + } + + private MethodSpec generaNotCallerMethod(boolean withException) { + MethodSpec.Builder builder = MethodSpec.methodBuilder("get" + (withException ? "WithException" : "")) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .returns(mStaticInterface); + String statement = "return $T.create($T.class, null, $L)"; + builder.addStatement(statement, + BR, + mStaticInterface, + withException + ); + return builder.build(); + } + + private MethodSpec generaCallerMethod(boolean withException) { + MethodSpec.Builder builder = MethodSpec.methodBuilder("get" + (withException ? "WithException" : "")) + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .addParameter(ClassName.get("java.lang", "Object"), "caller", Modifier.FINAL) + .returns(mContextInterface); + + String statement = "return $T.create($T.class, caller, $L)"; + builder.addStatement(statement, + BR, + mContextInterface, + withException + ); + return builder.build(); + } + + private MethodSpec generaIsLoadMethod() { + MethodSpec.Builder builder = MethodSpec.methodBuilder("getRealClass") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .returns(ClassName.get(Class.class)); + + String statement = "return top.niunaijun.blackreflection.utils.ClassUtil.classReady($T.class)"; + builder.addStatement(statement, + mContextInterface + ); + return builder.build(); + } +} diff --git a/compiler/src/main/java/top/niunaijun/blackreflection/utils/ClassUtils.java b/compiler/src/main/java/top/niunaijun/blackreflection/utils/ClassUtils.java new file mode 100644 index 0000000..4a2a27e --- /dev/null +++ b/compiler/src/main/java/top/niunaijun/blackreflection/utils/ClassUtils.java @@ -0,0 +1,30 @@ +package top.niunaijun.blackreflection.utils; + + +/** + * Created by sunwanquan on 2020/1/8. + * * ∧_∧ + * (`・ω・∥ + * 丶 つ0 + * しーJ + * 此处无Bug + */ +public class ClassUtils { + + public static String getPackage(String className) { + String[] sp = className.split("[.]"); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < sp.length - 1; i++) { + builder.append(sp[i]); + if (i != sp.length - 2) { + builder.append("."); + } + } + return builder.toString(); + } + + public static String getName(String className) { + String[] sp = className.split("[.]"); + return sp[sp.length - 1]; + } +} diff --git a/settings.gradle b/settings.gradle index b5d7487..26c1fb6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -34,4 +34,6 @@ dependencyResolutionManagement { rootProject.name = "NewBlackbox" include ':app' +include(":black-reflection") +include(":compiler") include ':Bcore'