diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/BlackBoxCore.java b/Bcore/src/main/java/top/niunaijun/blackbox/BlackBoxCore.java index aeb3859..1d22de2 100644 --- a/Bcore/src/main/java/top/niunaijun/blackbox/BlackBoxCore.java +++ b/Bcore/src/main/java/top/niunaijun/blackbox/BlackBoxCore.java @@ -1353,6 +1353,10 @@ public class BlackBoxCore extends ClientConfiguration { Context context = getContext(); String fileName = context.getPackageName() + "_logcat.txt"; boolean useMediaStore = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q; + + // Log comprehensive device information + logDeviceInfo(); + try { if (useMediaStore) { // Use MediaStore for Android 10+ @@ -1392,6 +1396,97 @@ public class BlackBoxCore extends ClientConfiguration { }).start(); } + /** + * Log comprehensive device information for debugging purposes + */ + private void logDeviceInfo() { + try { + Slog.i(TAG, "╔══════════════════════════════════════════════════════════════╗"); + Slog.i(TAG, "║ DEVICE INFORMATION ║"); + Slog.i(TAG, "╠══════════════════════════════════════════════════════════════╣"); + + // Android Version Info + Slog.i(TAG, "║ Android Version: " + Build.VERSION.RELEASE); + Slog.i(TAG, "║ SDK Level: " + Build.VERSION.SDK_INT); + Slog.i(TAG, "║ Build ID: " + Build.ID); + Slog.i(TAG, "║ Build Display: " + Build.DISPLAY); + + // Security Patch (API 23+) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + Slog.i(TAG, "║ Security Patch: " + Build.VERSION.SECURITY_PATCH); + } + + // Device Info + Slog.i(TAG, "╠══════════════════════════════════════════════════════════════╣"); + Slog.i(TAG, "║ Manufacturer: " + Build.MANUFACTURER); + Slog.i(TAG, "║ Brand: " + Build.BRAND); + Slog.i(TAG, "║ Model: " + Build.MODEL); + Slog.i(TAG, "║ Device: " + Build.DEVICE); + Slog.i(TAG, "║ Product: " + Build.PRODUCT); + Slog.i(TAG, "║ Board: " + Build.BOARD); + Slog.i(TAG, "║ Hardware: " + Build.HARDWARE); + + // CPU/ABI Info + Slog.i(TAG, "╠══════════════════════════════════════════════════════════════╣"); + Slog.i(TAG, "║ Supported ABIs: " + String.join(", ", Build.SUPPORTED_ABIS)); + if (Build.SUPPORTED_32_BIT_ABIS.length > 0) { + Slog.i(TAG, "║ 32-bit ABIs: " + String.join(", ", Build.SUPPORTED_32_BIT_ABIS)); + } + if (Build.SUPPORTED_64_BIT_ABIS.length > 0) { + Slog.i(TAG, "║ 64-bit ABIs: " + String.join(", ", Build.SUPPORTED_64_BIT_ABIS)); + } + + // Build Fingerprint + Slog.i(TAG, "╠══════════════════════════════════════════════════════════════╣"); + Slog.i(TAG, "║ Fingerprint: " + Build.FINGERPRINT); + Slog.i(TAG, "║ Type: " + Build.TYPE); + Slog.i(TAG, "║ Tags: " + Build.TAGS); + + // Memory Info + try { + Runtime runtime = Runtime.getRuntime(); + long maxMem = runtime.maxMemory() / (1024 * 1024); + long totalMem = runtime.totalMemory() / (1024 * 1024); + long freeMem = runtime.freeMemory() / (1024 * 1024); + Slog.i(TAG, "╠══════════════════════════════════════════════════════════════╣"); + Slog.i(TAG, "║ Max Heap: " + maxMem + " MB"); + Slog.i(TAG, "║ Total Heap: " + totalMem + " MB"); + Slog.i(TAG, "║ Free Heap: " + freeMem + " MB"); + Slog.i(TAG, "║ Used Heap: " + (totalMem - freeMem) + " MB"); + } catch (Exception e) { + Slog.w(TAG, "║ Memory info unavailable: " + e.getMessage()); + } + + // App Info + try { + Context context = getContext(); + if (context != null) { + Slog.i(TAG, "╠══════════════════════════════════════════════════════════════╣"); + Slog.i(TAG, "║ Package: " + context.getPackageName()); + android.content.pm.PackageInfo pInfo = context.getPackageManager() + .getPackageInfo(context.getPackageName(), 0); + Slog.i(TAG, "║ App Version: " + pInfo.versionName); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + Slog.i(TAG, "║ Version Code: " + pInfo.getLongVersionCode()); + } else { + Slog.i(TAG, "║ Version Code: " + pInfo.versionCode); + } + } + } catch (Exception e) { + Slog.w(TAG, "║ App info unavailable: " + e.getMessage()); + } + + // Timestamp + Slog.i(TAG, "╠══════════════════════════════════════════════════════════════╣"); + Slog.i(TAG, "║ Timestamp: " + new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss Z", + java.util.Locale.getDefault()).format(new java.util.Date())); + Slog.i(TAG, "╚══════════════════════════════════════════════════════════════╝"); + + } catch (Exception e) { + Slog.e(TAG, "Failed to log device info: " + e.getMessage()); + } + } + @SuppressWarnings("deprecation") private static String getProcessName(Context context) { int pid = Process.myPid(); @@ -1665,6 +1760,12 @@ public class BlackBoxCore extends ClientConfiguration { */ private void initVpnService() { try { + // Check if VPN mode is enabled in settings + if (mClientConfiguration == null || !mClientConfiguration.isUseVpnNetwork()) { + Slog.d(TAG, "VPN network mode disabled, using normal network"); + return; + } + // Start the VPN service asynchronously to prevent blocking main thread new Thread(new Runnable() { @Override diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/app/configuration/ClientConfiguration.java b/Bcore/src/main/java/top/niunaijun/blackbox/app/configuration/ClientConfiguration.java index 875b266..17442f3 100644 --- a/Bcore/src/main/java/top/niunaijun/blackbox/app/configuration/ClientConfiguration.java +++ b/Bcore/src/main/java/top/niunaijun/blackbox/app/configuration/ClientConfiguration.java @@ -28,6 +28,15 @@ public abstract class ClientConfiguration { return true; } + /** + * Whether to use VPN network mode for sandboxed apps. + * When enabled, network traffic is routed through the VPN service. + * Default is false (normal network mode). + */ + public boolean isUseVpnNetwork() { + return false; + } + /** * This method is called when an internal application requests to install a new application. * diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 573d08c..e69de29 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,56 +0,0 @@ -# Release Notes - NewBlackbox - -## Version: Latest Build (2026-01-31) - -### Bug Fixes - -#### Android 10 Black Screen Fix -**Problem:** Apps would show a black screen and timeout on Android 10 (API 29) due to initialization failures. - -**Root Cause:** -- `BRAttributionSource.getRealClass()` returns `null` on Android < 31 (AttributionSource was added in Android S) -- `SystemProviderStub.invoke()` crashed calling `.getName()` on null class -- `ClassInvocationStub.injectHook()` crashed when `getWho()` returned null for non-existent services - -**Solution:** -- Added null checks in `SystemProviderStub.java` before accessing `BRAttributionSource.getRealClass()` -- Added null check in `ClassInvocationStub.java` to skip hooks when services don't exist - -**Files Changed:** -- `Bcore/src/main/java/top/niunaijun/blackbox/fake/service/context/providers/SystemProviderStub.java` -- `Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/ClassInvocationStub.java` - ---- - -### Removed Features - -#### Xposed Framework Support Removed -- Removed `BXposedManagerService` and related AIDL interfaces -- Removed "Install Xposed Module" UI and Settings entries -- Removed Xposed enablement flags in `BlackBoxCore` and `ClientConfiguration` -- Cleaned up `AppSystemEnv` and repositories to remove Xposed package checks - ---- - -### 🔧 Stability Improvements - -#### Anti-Detection Native Hook Stability -**Problem:** Native file system hooks caused crashes due to infinite recursion. - -**Solution:** -- Removed `LOGD` calls from critical native hooks (`my_fopen`, `my_open`, `my_stat`, etc.) -- Fixed syntax errors in hook implementations -- Hooks now silently return `ENOENT` for blocked paths - ---- - -### Compatibility - -| Android Version | Status | -|-----------------|--------| -| Android 10 (Q) | ✅ Fixed | -| Android 11 (R) | ✅ Supported | -| Android 12 (S) | ✅ Supported | -| Android 13 (T) | ✅ Supported | -| Android 14 (U) | ✅ Supported | -| Android 15+ | ⚠️ Requires testing | 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 b291e65..6d6d089 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 @@ -25,6 +25,9 @@ class BlackBoxLoader { private var mDaemonEnable by AppSharedPreferenceDelegate(App.getContext(), false) private var mShowShortcutPermissionDialog by AppSharedPreferenceDelegate(App.getContext(), true) + // VPN network mode: true = use VPN, false = normal network + private var mUseVpnNetwork by AppSharedPreferenceDelegate(App.getContext(), false) + fun hideRoot(): Boolean { return try { mHideRoot @@ -76,6 +79,23 @@ class BlackBoxLoader { } } + fun useVpnNetwork(): Boolean { + return try { + mUseVpnNetwork + } catch (e: Exception) { + Log.e(TAG, "Error getting useVpnNetwork: ${e.message}") + false + } + } + + fun invalidUseVpnNetwork(enable: Boolean) { + try { + this.mUseVpnNetwork = enable + } catch (e: Exception) { + Log.e(TAG, "Error setting useVpnNetwork: ${e.message}") + } + } + fun getBlackBoxCore(): BlackBoxCore { return try { BlackBoxCore.get() @@ -219,6 +239,15 @@ class BlackBoxLoader { } } + override fun isUseVpnNetwork(): Boolean { + return try { + mUseVpnNetwork + } catch (e: Exception) { + Log.e(TAG, "Error checking useVpnNetwork: ${e.message}") + false + } + } + override fun requestInstallPackage( file: File?, userId: Int 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 e20e7da..2aa0fbc 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 @@ -3,6 +3,7 @@ package top.niunaijun.blackboxa.view.main import android.content.Context import android.content.Intent import android.net.Uri +import android.net.VpnService import android.os.Bundle import android.util.Log import android.view.Menu @@ -38,6 +39,7 @@ class MainActivity : LoadingActivity() { companion object { private const val TAG = "MainActivity" private const val STORAGE_PERMISSION_REQUEST_CODE = 1001 + private const val VPN_PERMISSION_REQUEST_CODE = 1002 fun start(context: Context) { val intent = Intent(context, MainActivity::class.java) @@ -64,6 +66,9 @@ class MainActivity : LoadingActivity() { // Check and request storage permission on Android 11+ checkStoragePermission() + // Check and request VPN permission + checkVpnPermission() + try { BlackBoxCore.get().onAfterMainActivityOnCreate(this) } catch (e: Exception) { @@ -197,6 +202,40 @@ class MainActivity : LoadingActivity() { } } + /** + * Check and request VPN permission if needed. VpnService.prepare() must be called from an + * Activity before VPN can be established. + */ + private fun checkVpnPermission() { + try { + val vpnIntent = VpnService.prepare(this) + if (vpnIntent != null) { + // User needs to grant VPN permission + Log.d(TAG, "VPN permission not granted, requesting...") + vpnPermissionResult.launch(vpnIntent) + } else { + // VPN permission already granted + Log.d(TAG, "VPN permission already granted") + } + } catch (e: Exception) { + Log.e(TAG, "Error checking VPN permission: ${e.message}") + } + } + + private val vpnPermissionResult = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + try { + if (result.resultCode == RESULT_OK) { + Log.d(TAG, "VPN permission granted!") + // VPN service will now be able to establish connection + } else { + Log.w(TAG, "VPN permission denied by user") + } + } catch (e: Exception) { + Log.e(TAG, "Error handling VPN permission result: ${e.message}") + } + } + private fun showErrorDialog(message: String) { try { MaterialDialog(this).show { diff --git a/app/src/main/java/top/niunaijun/blackboxa/view/setting/SettingFragment.kt b/app/src/main/java/top/niunaijun/blackboxa/view/setting/SettingFragment.kt index b850eec..d72c337 100644 --- a/app/src/main/java/top/niunaijun/blackboxa/view/setting/SettingFragment.kt +++ b/app/src/main/java/top/niunaijun/blackboxa/view/setting/SettingFragment.kt @@ -9,12 +9,6 @@ import top.niunaijun.blackboxa.app.AppManager import top.niunaijun.blackboxa.util.toast import top.niunaijun.blackboxa.view.gms.GmsManagerActivity -/** - * - * @Description: - * @Author: wukaicheng - * @CreateDate: 2021/5/6 22:13 - */ class SettingFragment : PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { @@ -35,6 +29,13 @@ class SettingFragment : PreferenceFragmentCompat() { daemonPreference.setDefaultValue(mDaemonEnable) daemonPreference } + + invalidHideState { + val vpnPreference: Preference = (findPreference("use_vpn_network")!!) + val mUseVpnNetwork = AppManager.mBlackBoxLoader.useVpnNetwork() + vpnPreference.setDefaultValue(mUseVpnNetwork) + vpnPreference + } } private fun initGms() { @@ -64,6 +65,9 @@ class SettingFragment : PreferenceFragmentCompat() { "daemon_enable" -> { AppManager.mBlackBoxLoader.invalidDaemonEnable(tmpHide) } + "use_vpn_network" -> { + AppManager.mBlackBoxLoader.invalidUseVpnNetwork(tmpHide) + } } toast(R.string.restart_module) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 12cb86e..3abb270 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -56,6 +56,8 @@ Please uninstall the xposed module in the Module Manager Others Daemon + Use VPN Network + Route traffic through VPN for sandboxed apps (may improve network access) GMS Manager Go to Gms Manager Enable Service diff --git a/app/src/main/res/xml/setting.xml b/app/src/main/res/xml/setting.xml index db703e4..4901892 100644 --- a/app/src/main/res/xml/setting.xml +++ b/app/src/main/res/xml/setting.xml @@ -21,6 +21,11 @@ app:key="daemon_enable" app:title="@string/daemon_enable" /> + + \ No newline at end of file