Android Studio中如何在已Root设备上绕过权限限制读写受保护目录的JSON文件
Great question! The core issue here is that standard FileWriter/FileReader operations run in your app's unprivileged user context—even if your app has been granted root access, these IO calls don't automatically escalate privileges. To write/read to restricted paths like /data/misc/com.crjase.systemuihook/properties.json on Android 15, you need to explicitly leverage root privileges via a su shell process.
Here's a step-by-step solution tailored to your existing code:
1. Create a Root File Utilities Class
First, build a helper class to handle file operations via root shell. This avoids repeating code and ensures safe handling of special characters in JSON content:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; public class RootFileUtils { private static final String TAG = "RootFileUtils"; // Write content to a file using root privileges public static boolean writeFileWithRoot(String filePath, String content) { Process process = null; OutputStreamWriter outputStreamWriter = null; try { // Launch a su shell process process = Runtime.getRuntime().exec("su"); outputStreamWriter = new OutputStreamWriter(process.getOutputStream()); // Ensure parent directory exists (create with root if needed) String parentDir = filePath.substring(0, filePath.lastIndexOf('/')); outputStreamWriter.write("mkdir -p " + parentDir + "\n"); // Write JSON content safely using cat (avoids issues with quotes/newlines) outputStreamWriter.write("cat > " + filePath + " <<EOF\n"); outputStreamWriter.write(content); outputStreamWriter.write("\nEOF\n"); outputStreamWriter.flush(); // Exit the shell and verify success outputStreamWriter.write("exit\n"); outputStreamWriter.flush(); int exitCode = process.waitFor(); return exitCode == 0; } catch (IOException | InterruptedException e) { android.util.Log.e(TAG, "Root write failed: " + e.getMessage()); return false; } finally { // Clean up resources try { if (outputStreamWriter != null) outputStreamWriter.close(); if (process != null) process.destroy(); } catch (IOException e) { android.util.Log.e(TAG, "Resource cleanup error: " + e.getMessage()); } } } // Read content from a file using root privileges public static String readFileWithRoot(String filePath) { Process process = null; BufferedReader bufferedReader = null; try { // Execute cat command via su shell process = Runtime.getRuntime().exec("su -c cat \"" + filePath + "\""); bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream())); // Read the output into a string StringBuilder contentBuilder = new StringBuilder(); String line; while ((line = bufferedReader.readLine()) != null) { contentBuilder.append(line).append("\n"); } // Check if the command succeeded int exitCode = process.waitFor(); if (exitCode != 0) { // Log error details from the shell's stderr BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); StringBuilder errorMsg = new StringBuilder(); String errorLine; while ((errorLine = errorReader.readLine()) != null) errorMsg.append(errorLine); android.util.Log.e(TAG, "Root read failed (exit code: " + exitCode + "): " + errorMsg); return null; } // Trim trailing newline if (contentBuilder.length() > 0) contentBuilder.setLength(contentBuilder.length() - 1); return contentBuilder.toString(); } catch (IOException | InterruptedException e) { android.util.Log.e(TAG, "Root read failed: " + e.getMessage()); return null; } finally { // Clean up resources try { if (bufferedReader != null) bufferedReader.close(); if (process != null) process.destroy(); } catch (IOException e) { android.util.Log.e(TAG, "Resource cleanup error: " + e.getMessage()); } } } }
2. Modify Your Existing Methods to Use Root Utilities
Update your saveProperties and loadProperties methods to rely on the root helper class instead of standard IO:
Updated Save Method
private static final String FILEPATH = "/data/misc/com.crjase.systemuihook/properties.json"; private final Gson gson = new Gson(); private static final String TAG = "PropertiesManager"; // Match your existing TAG public void saveProperties(UIProperties properties) { // Serialize properties to a JSON string first String jsonContent = gson.toJson(properties); boolean writeSuccess = RootFileUtils.writeFileWithRoot(FILEPATH, jsonContent); if (writeSuccess) { android.util.Log.i(TAG, "UI Properties saved to " + FILEPATH); } else { android.util.Log.e(TAG, "Failed to save UI Properties to " + FILEPATH + " (root operation failed)"); } }
Updated Load Method
public UIProperties loadProperties() { String jsonContent = RootFileUtils.readFileWithRoot(FILEPATH); if (jsonContent == null) { android.util.Log.e(TAG, "Could not load UI properties from " + FILEPATH + " (root read failed)"); return new UIProperties(); } try { UIProperties properties = gson.fromJson(jsonContent, UIProperties.class); if (properties == null) { android.util.Log.e(TAG, "JSON file " + FILEPATH + " is empty or invalid. Returning new UIProperties."); return new UIProperties(); } return properties; } catch (JsonSyntaxException e) { android.util.Log.e(TAG, "Invalid JSON syntax in " + FILEPATH); return new UIProperties(); } }
3. Critical Notes for Android 15 & Rooted Devices
- Verify Root Access First: Before using these methods, add a check to confirm your app can obtain root privileges. For example, execute
su -c idand verify the output containsuid=0(root). - SELinux Restrictions: Android 15 enforces strict SELinux policies even on rooted devices. If you still hit permission errors, you may need to temporarily disable SELinux (run
su -c setenforce 0) or adjust the file's SELinux context (e.g.,su -c chcon u:object_r:system_data_file:s0 " + FILEPATH). Note that disabling SELinux is not recommended for long-term use. - File Permissions: After writing the file, if other system processes (like SystemUI) need to read it, adjust permissions with
su -c chmod 644 " + FILEPATH(add this line to thewriteFileWithRootmethod after thecatcommand). - Special Character Safety: Using
cat <<EOFinstead ofechoensures your JSON content (which may include quotes, newlines, or special symbols) is written correctly without corruption.
内容来源于stack exchange

