From b93acb1f99a8340539e190bfbfe445fa9992c55a Mon Sep 17 00:00:00 2001 From: Ken Van Hoeylandt Date: Wed, 19 Aug 2026 21:45:40 +0200 Subject: [PATCH] Fix for app uninstall --- Modules/app-module/include/app/manager.h | 8 +++ .../app-module/private/app/private/app_fs.h | 45 ++++++++++++ Modules/app-module/source/app_install.cpp | 72 +++++-------------- Modules/app-module/source/app_paths.cpp | 13 +++- Modules/app-module/source/manager.cpp | 26 +++++++ Modules/app-module/source/symbols.cpp | 1 + .../app/apphubdetails/AppHubDetailsApp.cpp | 20 ++---- 7 files changed, 115 insertions(+), 70 deletions(-) diff --git a/Modules/app-module/include/app/manager.h b/Modules/app-module/include/app/manager.h index f535282a8..2f2c1d76a 100644 --- a/Modules/app-module/include/app/manager.h +++ b/Modules/app-module/include/app/manager.h @@ -152,6 +152,14 @@ error_t app_manager_install_path_add(const char* path); */ void app_manager_install_path_scan(void); +/** + * Uninstalls an app that was registered via app_manager_install_path_scan() (i.e. discovered on + * disk, not installed via app_install()). Stops running instances, removes the manifest + * registration, and deletes the app directory. Returns ERROR_NOT_FOUND if the app id is not in + * the scan registry. + */ +error_t app_manager_install_path_uninstall(const char* app_id); + #ifdef __cplusplus } #endif diff --git a/Modules/app-module/private/app/private/app_fs.h b/Modules/app-module/private/app/private/app_fs.h index d4dff2a14..1c04faa00 100644 --- a/Modules/app-module/private/app/private/app_fs.h +++ b/Modules/app-module/private/app/private/app_fs.h @@ -12,6 +12,7 @@ #include #include #include +#include #include inline bool app_fs_is_directory(const std::string& path) { @@ -36,6 +37,50 @@ inline bool app_fs_is_file(const std::string& path) { // Appends the full path of every direct subdirectory of @a path to @a out. // No-op (not an error) if @a path can't be opened. +inline bool app_fs_delete_recursively(const std::string& path) { + if (path.empty() || path == "/" || path == "." || path == "..") { + return true; + } + + if (app_fs_is_directory(path)) { + FileMutex file_mutex; + file_mutex_get(&file_mutex, path.c_str()); + file_mutex_lock(&file_mutex); + + DIR* dir = opendir(path.c_str()); + if (dir == nullptr) { + file_mutex_unlock(&file_mutex); + return false; + } + + bool success = true; + struct dirent* entry; + while (success && (entry = readdir(dir)) != nullptr) { + if (std::strcmp(entry->d_name, ".") == 0 || std::strcmp(entry->d_name, "..") == 0) { + continue; + } + success = app_fs_delete_recursively(path + "/" + entry->d_name); + } + closedir(dir); + + if (!success) { + file_mutex_unlock(&file_mutex); + return false; + } + + bool result = rmdir(path.c_str()) == 0; + file_mutex_unlock(&file_mutex); + return result; + } + + FileMutex file_mutex; + file_mutex_get(&file_mutex, path.c_str()); + file_mutex_lock(&file_mutex); + bool result = unlink(path.c_str()) == 0; + file_mutex_unlock(&file_mutex); + return result; +} + inline void app_fs_list_direct_subdirectories(const std::string& path, std::vector& out) { // Collect child names while the directory lock is held, then release it before classifying // each one with app_fs_is_directory() - that function looks up and locks a FileMutex too, diff --git a/Modules/app-module/source/app_install.cpp b/Modules/app-module/source/app_install.cpp index fd2ea0298..8d7820099 100644 --- a/Modules/app-module/source/app_install.cpp +++ b/Modules/app-module/source/app_install.cpp @@ -66,57 +66,8 @@ bool ensure_directory_recursive(const std::string& path) { } bool delete_recursively(const std::string& path) { - LOG_D(TAG, "Deleting %s...", path.c_str()); - if (path.empty() || path == "/" || path == "." || path == "..") { - return true; - } - - if (app_fs_is_directory(path)) { - LOG_D(TAG, "Deleting dir %s", path.c_str()); - - FileMutex file_mutex; - file_mutex_get(&file_mutex, path.c_str()); - file_mutex_lock(&file_mutex); - - DIR* dir = opendir(path.c_str()); - if (dir == nullptr) { - LOG_E(TAG, "Failed to scan directory %s", path.c_str()); - file_mutex_unlock(&file_mutex); - return false; - } - - bool success = true; - dirent* entry; - while (success && (entry = readdir(dir)) != nullptr) { - if (std::strcmp(entry->d_name, ".") == 0 || std::strcmp(entry->d_name, "..") == 0) { - continue; - } - success = delete_recursively(path + "/" + entry->d_name); - } - closedir(dir); - - if (!success) { - file_mutex_unlock(&file_mutex); - return false; - } - - bool result = rmdir(path.c_str()) == 0; - file_mutex_unlock(&file_mutex); - return result; - } - - if (app_fs_is_file(path)) { - LOG_D(TAG, "Deleting file %s", path.c_str()); - FileMutex mutex {}; - file_mutex_get(&mutex, path.c_str()); - file_mutex_lock(&mutex); - bool result = remove(path.c_str()) == 0; - file_mutex_unlock(&mutex); - return result; - } - - LOG_D(TAG, "Deleting done"); - return true; + LOG_I(TAG, "Deleting %s...", path.c_str()); + return app_fs_delete_recursively(path); } bool get_app_install_directory(std::string& out_path) { @@ -277,6 +228,11 @@ error_t uninstall_locked(const std::string& app_id) { return ERROR_NOT_FOUND; } + // Can't uninstall in-memory apps + if (iterator->second->manifest.location.type != APP_LOCATION_PATH) { + return ERROR_NOT_SUPPORTED; + } + stop_all_instances_of(&iterator->second->manifest); app_manager_remove(app_id.c_str()); delete_recursively(iterator->second->path); @@ -404,10 +360,20 @@ error_t app_uninstall(const char* app_id) { auto& registry = install_registry(); mutex_lock(®istry.mutex); - error_t result = uninstall_locked(app_id); + error_t error = uninstall_locked(app_id); mutex_unlock(®istry.mutex); - return result; + if (error == ERROR_NOT_FOUND) { + error = app_manager_install_path_uninstall(app_id); + } + + if (error == ERROR_NONE) { + LOG_I(TAG, "Uninstalled %s", app_id); + } else { + LOG_I(TAG, "Uninstalling %s failed: %s", app_id, error_to_string(error)); + } + + return error; } } // extern "C" diff --git a/Modules/app-module/source/app_paths.cpp b/Modules/app-module/source/app_paths.cpp index fb102ac0a..8d4e1a9a1 100644 --- a/Modules/app-module/source/app_paths.cpp +++ b/Modules/app-module/source/app_paths.cpp @@ -1,5 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 +#include +#include #include #include @@ -34,12 +36,17 @@ error_t app_paths_get_user_data_path(const char* app_id, const char* child_path, } error_t app_paths_get_assets_directory(const char* app_id, char* out_path, size_t out_path_size) { - char directory[224]; - error_t error = app_paths_get_user_data_directory(app_id, directory, sizeof(directory)); + AppManifest manifest; + error_t error = app_manager_find_manifest(app_id, &manifest); if (error != ERROR_NONE) { return error; } - int written = std::snprintf(out_path, out_path_size, "%s/assets", directory); + + if (manifest.location.type != APP_LOCATION_PATH) { + return ERROR_NOT_FOUND; + } + + int written = std::snprintf(out_path, out_path_size, "%s/assets", static_cast(manifest.location.location)); if (written < 0 || (size_t)written >= out_path_size) { return ERROR_BUFFER_OVERFLOW; } diff --git a/Modules/app-module/source/manager.cpp b/Modules/app-module/source/manager.cpp index 8db6489a0..3786f7d21 100644 --- a/Modules/app-module/source/manager.cpp +++ b/Modules/app-module/source/manager.cpp @@ -340,4 +340,30 @@ void app_manager_install_path_scan(void) { mutex_unlock(®istry.mutex); } +error_t app_manager_install_path_uninstall(const char* app_id) { + auto& registry = install_path_registry(); + + mutex_lock(®istry.mutex); + auto iterator = registry.scanned.find(app_id); + if (iterator == registry.scanned.end()) { + mutex_unlock(®istry.mutex); + return ERROR_NOT_FOUND; + } + + auto path = iterator->second->path; + mutex_unlock(®istry.mutex); + + // app_manager_remove takes ledger.mutex internally - call outside registry.mutex + // to match the lock ordering in app_manager_install_path_scan(). + app_manager_remove(app_id); + + mutex_lock(®istry.mutex); + registry.scanned.erase(app_id); + mutex_unlock(®istry.mutex); + + app_fs_delete_recursively(path); + + return ERROR_NONE; +} + } // extern "C" diff --git a/Modules/app-module/source/symbols.cpp b/Modules/app-module/source/symbols.cpp index 324f0606f..808d7404e 100644 --- a/Modules/app-module/source/symbols.cpp +++ b/Modules/app-module/source/symbols.cpp @@ -40,6 +40,7 @@ const ModuleSymbol app_module_symbols[] = { DEFINE_MODULE_SYMBOL(app_manager_get_topmost_app_id), DEFINE_MODULE_SYMBOL(app_manager_install_path_add), DEFINE_MODULE_SYMBOL(app_manager_install_path_scan), + DEFINE_MODULE_SYMBOL(app_manager_install_path_uninstall), // app/metadata DEFINE_MODULE_SYMBOL(app_metadata_parse), // app/paths diff --git a/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp b/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp index 4fd9117e1..ea7581421 100644 --- a/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp +++ b/Tactility/Source/app/apphubdetails/AppHubDetailsApp.cpp @@ -152,27 +152,19 @@ void updateApp(Context* ctx) { void updateViews(Context* ctx) { lvgl_toolbar_clear_actions(ctx->toolbar); auto app_id = ctx->entry.appId.c_str(); - AppManifest manifest; - bool is_installed = app_manager_find_manifest(app_id, &manifest) == ERROR_NONE; ctx->spinner = lvgl_toolbar_add_spinner_action(ctx->toolbar); lv_obj_add_flag(ctx->spinner, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(ctx->updateLabel, LV_OBJ_FLAG_HIDDEN); char install_path[128]; - if (app_get_install_path(app_id, install_path, sizeof(install_path)) != ERROR_NONE) { - LOG_E(TAG, "Install path not found for %s", app_id); - return; - } - - std::string metadata_path = std::string(install_path) + "/manifest.properties"; - AppMetadata metadata; - if (app_metadata_parse(metadata_path.c_str(), &metadata) != ERROR_NONE) { - LOG_E(TAG, "Failed to parse metadata at %s", metadata_path.c_str()); - return; - } + bool is_installed = app_get_install_path(app_id, install_path, sizeof(install_path)) == ERROR_NONE + && file::isFile(std::string(install_path) + "/manifest.properties"); if (is_installed) { - if (metadata.app_version_code < ctx->entry.appVersionCode) { + std::string metadata_path = std::string(install_path) + "/manifest.properties"; + AppMetadata metadata; + if (app_metadata_parse(metadata_path.c_str(), &metadata) == ERROR_NONE + && metadata.app_version_code < ctx->entry.appVersionCode) { ctx->updateButton = lvgl_toolbar_add_image_button_action(ctx->toolbar, LV_SYMBOL_DOWNLOAD, onUpdatePressed, ctx); lv_obj_remove_flag(ctx->updateLabel, LV_OBJ_FLAG_HIDDEN); }