diff --git a/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/ClassInvocationStub.java b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/ClassInvocationStub.java
index 1aaf8aa..a5da868 100644
--- a/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/ClassInvocationStub.java
+++ b/Bcore/src/main/java/top/niunaijun/blackbox/fake/hook/ClassInvocationStub.java
@@ -50,6 +50,10 @@ public abstract class ClassInvocationStub implements InvocationHandler, IInjectH
@Override
public void injectHook() {
mBase = getWho();
+ // Skip hook if service doesn't exist on this Android version
+ if (mBase == null) {
+ return;
+ }
mProxyInvocation = Proxy.newProxyInstance(mBase.getClass().getClassLoader(), MethodParameterUtils.getAllInterface(mBase.getClass()), this);
if (!onlyProxy) {
inject(mBase, mProxyInvocation);
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 8651216..67eb09f 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
@@ -91,7 +91,7 @@ import top.niunaijun.blackbox.utils.compat.BuildCompat;
import top.niunaijun.blackbox.fake.service.ISettingsProviderProxy;
import top.niunaijun.blackbox.fake.service.FeatureFlagUtilsProxy;
import top.niunaijun.blackbox.fake.service.WorkManagerProxy;
-import top.niunaijun.blackbox.fake.service.IInputMethodManagerProxy;
+// Removed IInputMethodManagerProxy - causes ClassCastException on Android 15
/**
* updated by alex5402 on 3/30/21.
@@ -177,7 +177,7 @@ public class HookManager {
addInjector(new IMediaRouterServiceProxy());
addInjector(new IPowerManagerProxy());
addInjector(new IContextHubServiceProxy());
- addInjector(new IInputMethodManagerProxy());
+ // Removed IInputMethodManagerProxy - causes ClassCastException on Android 15 due to queryLocalInterface returning non-IInterface proxy
addInjector(new IVibratorServiceProxy());
addInjector(new IPersistentDataBlockServiceProxy());
addInjector(AppInstrumentation.get());
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 d71531e..6492d70 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
@@ -60,9 +60,12 @@ public class SystemProviderStub extends ClassInvocationStub implements BContentP
if ("call".equals(methodName)) {
// Only fix AttributionSource in call() args, don't replace method name
if (args != null) {
+ Class> attributionSourceClass = BRAttributionSource.getRealClass();
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
- if (arg != null && arg.getClass().getName().equals(BRAttributionSource.getRealClass().getName())) {
+ // Check for null - AttributionSource doesn't exist on Android < S (31)
+ if (arg != null && attributionSourceClass != null &&
+ arg.getClass().getName().equals(attributionSourceClass.getName())) {
ContextCompat.fixAttributionSourceState(arg, BlackBoxCore.getHostUid());
}
}
@@ -79,8 +82,12 @@ public class SystemProviderStub extends ClassInvocationStub implements BContentP
if (!isSystemProviderAuthority(authority)) {
args[0] = BlackBoxCore.getHostPkg();
}
- } else if (arg != null && arg.getClass().getName().equals(BRAttributionSource.getRealClass().getName())) {
- ContextCompat.fixAttributionSourceState(arg, BlackBoxCore.getHostUid());
+ } else if (arg != null) {
+ Class> attrSourceClass = BRAttributionSource.getRealClass();
+ // Check for null - AttributionSource doesn't exist on Android < S (31)
+ if (attrSourceClass != null && arg.getClass().getName().equals(attrSourceClass.getName())) {
+ ContextCompat.fixAttributionSourceState(arg, BlackBoxCore.getHostUid());
+ }
}
}
return method.invoke(mBase, args);
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index e71a85e..573d08c 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -1,31 +1,56 @@
# Release Notes - NewBlackbox
-## Version: Latest Build (2026-01-30)
+## Version: Latest Build (2026-01-31)
-### Major Changes
+### Bug Fixes
-#### 1. Removal of Xposed Framework Support
-**Summary:** The Xposed module loading and management features have been completely removed from BlackBox to streamline the core and remove legacy dependencies.
-**Details:**
-- 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.
-- Removed `FLAG_XPOSED` from `InstallOption` and `USER_XPOSED` from `BUserHandle`.
+#### 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
-#### 2. Anti-Detection Stability Fix
-**Problem:** The native file system hooks in `AntiDetection.cpp` were causing application crashes due to infinite recursion (Hook calls logging -> Logging calls `open` -> `open` hook calls Logging).
**Solution:**
-- Removed all `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 without triggering recursion.
+- Added null checks in `SystemProviderStub.java` before accessing `BRAttributionSource.getRealClass()`
+- Added null check in `ClassInvocationStub.java` to skip hooks when services don't exist
-### Technical Details
+**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`
-**Refactoring:**
-- Deleted `Bcore/src/main/java/top/niunaijun/blackbox/fake/frameworks/BXposedManager.java`
-- Deleted `app/src/main/java/top/niunaijun/blackboxa/view/xp` (Xposed UI)
-- Fixed compilation errors in `BlackBoxCore.java` and `AppSystemEnv.java` resulting from the removal.
+---
-**Known Limitations:**
-- **Native Signature Spoofing:** While `AntiDetection` hooks are stable, fully spoofing the APK signature at the native level (for apps that read the APK file directly) is not fully implemented. The current hooks hide blocked files but do not redirect file access to the original APK. Implementing this requires **PLT Hooking** (e.g., via xHook) and knowledge of the original APK path.
\ No newline at end of file
+### 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/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3fcf9bf..80b647d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -6,6 +6,12 @@
+
+
+
+
+
= android.os.Build.VERSION_CODES.R) {
+ // Android 11+: Need MANAGE_EXTERNAL_STORAGE
if (!android.os.Environment.isExternalStorageManager()) {
Log.w(TAG, "MANAGE_EXTERNAL_STORAGE permission not granted")
showStoragePermissionDialog()
}
+ } else {
+ // Android 10 and below: Need READ/WRITE_EXTERNAL_STORAGE
+ if (androidx.core.content.ContextCompat.checkSelfPermission(
+ this,
+ android.Manifest.permission.WRITE_EXTERNAL_STORAGE
+ ) != android.content.pm.PackageManager.PERMISSION_GRANTED ||
+ androidx.core.content.ContextCompat.checkSelfPermission(
+ this,
+ android.Manifest.permission.READ_EXTERNAL_STORAGE
+ ) != android.content.pm.PackageManager.PERMISSION_GRANTED
+ ) {
+ Log.w(
+ TAG,
+ "Storage permissions not granted on Android ${android.os.Build.VERSION.SDK_INT}"
+ )
+ requestLegacyStoragePermission()
+ }
}
} catch (e: Exception) {
Log.e(TAG, "Error checking storage permission: ${e.message}")
}
}
+ private fun requestLegacyStoragePermission() {
+ try {
+ androidx.core.app.ActivityCompat.requestPermissions(
+ this,
+ arrayOf(
+ android.Manifest.permission.READ_EXTERNAL_STORAGE,
+ android.Manifest.permission.WRITE_EXTERNAL_STORAGE
+ ),
+ STORAGE_PERMISSION_REQUEST_CODE
+ )
+ } catch (e: Exception) {
+ Log.e(TAG, "Error requesting storage permission: ${e.message}")
+ }
+ }
+
+ override fun onRequestPermissionsResult(
+ requestCode: Int,
+ permissions: Array,
+ grantResults: IntArray
+ ) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ if (requestCode == STORAGE_PERMISSION_REQUEST_CODE) {
+ if (grantResults.isNotEmpty() &&
+ grantResults.all {
+ it == android.content.pm.PackageManager.PERMISSION_GRANTED
+ }
+ ) {
+ Log.d(TAG, "Storage permissions granted")
+ } else {
+ Log.w(TAG, "Storage permissions denied")
+ }
+ }
+ }
+
private fun showStoragePermissionDialog() {
try {
MaterialDialog(this).show {
diff --git a/build.gradle b/build.gradle
index 9cad877..f42bd9e 100644
--- a/build.gradle
+++ b/build.gradle
@@ -11,7 +11,7 @@ ext {
// targetSdkVersion = 35
minSdk = 21
versionCode = 1
- versionName = "3.0.13-beta2"
+ versionName = "3.1.0-beta"
xVersion = '1.1.0'
hiddenApiBypass = '4.3'
}
\ No newline at end of file