fix files directory handling: use internal storage and add exception handling

- Use internal files dir instead of external (fixes scoped storage permissions)
- Pass files directory from Android context via JNI instead of hardcoding
- Add exception handling in ScanAppsDirectory to prevent crashes on permission errors
- Use std::error_code overload of fs::exists() to avoid throwing on access denial

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-19 21:49:58 +01:00
parent 76d97e202b
commit 8cf24d8c2a
4 changed files with 113 additions and 68 deletions

View File

@@ -585,80 +585,94 @@ bool AppManager::DownloadFile(const std::string& url, const std::string& dest_pa
void AppManager::ScanAppsDirectory() {
std::string apps_dir = m_data_root + "/apps";
if (!fs::exists(apps_dir)) {
try {
if (!fs::exists(apps_dir)) {
return;
}
} catch (const std::exception& e) {
LOG_ERROR("Cannot access apps directory: %s", e.what());
return;
}
LOG_INFO("Scanning apps directory: %s", apps_dir.c_str());
for (const auto& entry : fs::directory_iterator(apps_dir)) {
if (!entry.is_directory()) {
continue;
}
std::string package_id = entry.path().filename().string();
// Skip if already registered
if (m_installed_apps.find(package_id) != m_installed_apps.end()) {
continue;
}
// Look for manifest.json in direct folder or package/ subfolder
std::string manifest_path = entry.path().string() + "/manifest.json";
std::string manifest_path_pkg = entry.path().string() + "/package/manifest.json";
std::string actual_manifest;
std::string app_base_path;
if (fs::exists(manifest_path)) {
actual_manifest = manifest_path;
app_base_path = entry.path().string();
} else if (fs::exists(manifest_path_pkg)) {
actual_manifest = manifest_path_pkg;
app_base_path = entry.path().string() + "/package";
} else {
continue;
}
// Parse manifest
std::ifstream mf(actual_manifest);
if (!mf) {
continue;
}
try {
json j;
mf >> j;
InstalledApp app;
app.package_id = j.value("id", package_id);
app.name = j.value("name", package_id);
app.version_name = j.value("version", "1.0.0");
app.version_code = j.value("version_code", 1);
app.install_path = entry.path().string();
app.entry_point = j.value("entry", "main.rml");
app.icon_path = j.value("icon", "");
app.is_system_app = false;
if (j.contains("developer")) {
app.developer_name = j["developer"].value("name", "");
}
if (j.contains("permissions") && j["permissions"].is_array()) {
for (const auto& perm : j["permissions"]) {
app.permissions.push_back(perm.get<std::string>());
try {
for (const auto& entry : fs::directory_iterator(apps_dir)) {
try {
if (!entry.is_directory()) {
continue;
}
std::string package_id = entry.path().filename().string();
// Skip if already registered
if (m_installed_apps.find(package_id) != m_installed_apps.end()) {
continue;
}
// Look for manifest.json in direct folder or package/ subfolder
std::string manifest_path = entry.path().string() + "/manifest.json";
std::string manifest_path_pkg = entry.path().string() + "/package/manifest.json";
std::string actual_manifest;
std::string app_base_path;
// Use error_code overload to avoid exceptions on permission errors
std::error_code ec;
if (fs::exists(manifest_path, ec) && !ec) {
actual_manifest = manifest_path;
app_base_path = entry.path().string();
} else if (fs::exists(manifest_path_pkg, ec) && !ec) {
actual_manifest = manifest_path_pkg;
app_base_path = entry.path().string() + "/package";
} else {
if (ec) {
LOG_WARN("Cannot access %s: %s", package_id.c_str(), ec.message().c_str());
}
continue;
}
// Parse manifest
std::ifstream mf(actual_manifest);
if (!mf) {
continue;
}
json j;
mf >> j;
InstalledApp app;
app.package_id = j.value("id", package_id);
app.name = j.value("name", package_id);
app.version_name = j.value("version", "1.0.0");
app.version_code = j.value("version_code", 1);
app.install_path = entry.path().string();
app.entry_point = j.value("entry", "main.rml");
app.icon_path = j.value("icon", "");
app.is_system_app = false;
if (j.contains("developer")) {
app.developer_name = j["developer"].value("name", "");
}
if (j.contains("permissions") && j["permissions"].is_array()) {
for (const auto& perm : j["permissions"]) {
app.permissions.push_back(perm.get<std::string>());
}
}
app.installed_at = std::chrono::system_clock::now();
app.updated_at = std::chrono::system_clock::now();
m_installed_apps[app.package_id] = app;
LOG_INFO("Discovered app: %s (%s)", app.name.c_str(), app.package_id.c_str());
} catch (const std::exception& e) {
LOG_WARN("Error processing app entry: %s", e.what());
}
app.installed_at = std::chrono::system_clock::now();
app.updated_at = std::chrono::system_clock::now();
m_installed_apps[app.package_id] = app;
LOG_INFO("Discovered app: %s (%s)", app.name.c_str(), app.package_id.c_str());
} catch (const std::exception& e) {
LOG_WARN("Failed to parse manifest for %s: %s", package_id.c_str(), e.what());
}
} catch (const std::exception& e) {
LOG_ERROR("Error scanning apps directory: %s", e.what());
}
}

View File

@@ -27,6 +27,9 @@
#include <mosis/sandbox/crypto_api.h>
#include <mosis/sandbox/virtual_fs.h>
// External function to get files directory from JNI
extern const std::string& GetFilesDir();
// Global state for Lua access
static Rml::Context* g_context = nullptr;
static Rml::ElementDocument* g_document = nullptr;
@@ -500,9 +503,15 @@ void Kernel::main_loop()
Logger::Log("RmlUi Lua bindings initialized");
// Initialize app management system
// TODO: Get data root from Android context (for now use a placeholder)
std::string data_root = "/data/data/com.omixlab.mosis/files";
// Get files directory from Android context (set via JNI)
std::string data_root = GetFilesDir();
if (data_root.empty()) {
// Fallback if not set (shouldn't happen in normal operation)
data_root = "/data/data/com.omixlab.mosis/files";
Logger::Log("Warning: Files directory not set, using fallback");
}
g_data_root = data_root; // Store for sandbox switching
Logger::Log("Using data root: " + data_root);
m_app_manager = std::make_unique<mosis::AppManager>(data_root);
m_update_service = std::make_unique<mosis::UpdateService>(
m_app_manager.get(), "https://portal.mosis.dev/api/v1");

View File

@@ -83,4 +83,22 @@ Java_com_omixlab_mosis_NativeService_setAssetManager(JNIEnv *env, jobject thiz,
jobject asset_manager)
{
AssetsManager::Init(AAssetManager_fromJava(env, asset_manager));
}
// Global storage for files directory path
static std::string g_files_dir;
extern "C"
JNIEXPORT void JNICALL
Java_com_omixlab_mosis_NativeService_setFilesDir(JNIEnv *env, jobject thiz,
jstring files_dir)
{
const char* path = env->GetStringUTFChars(files_dir, nullptr);
g_files_dir = path;
env->ReleaseStringUTFChars(files_dir, path);
LOG_INFO("Files directory set to: %s", g_files_dir.c_str());
}
const std::string& GetFilesDir() {
return g_files_dir;
}

View File

@@ -19,6 +19,7 @@ class NativeService : Service() {
}
external fun getBinderNative(): IBinder
external fun setAssetManager(assetManager: AssetManager)
external fun setFilesDir(filesDir: String)
override fun onBind(intent: Intent): IBinder {
return getBinderNative()
@@ -27,6 +28,9 @@ class NativeService : Service() {
override fun onCreate() {
super.onCreate()
setAssetManager(assets)
// Use internal files dir - more reliable access in service processes
// For debugging, use: adb shell run-as com.omixlab.mosis ls files/
setFilesDir(filesDir.absolutePath)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {