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