Merge develop into main (#343)
- Refactor `AppManifest`: add new fields and rename existing ones - Parse and validate the manifest from an app that is being installed. - Remove deprecated `scoped()` from `Lock` - Create `Tactility/Paths.h` - App loading at boot now properly parses the manifest files of external apps - Properly lock both source and destination locations during app install - Remove LVGL path variants from `AppPaths` and `ServicePaths` - Removed `xPath` base classes for apps and services. There's now `AppPaths` and `ServicePaths`. - Renamed app and service paths: "data" and "system" paths are now "user data" and "assets"
This commit is contained in:
committed by
GitHub
parent
a4d15b2a1e
commit
bab3eb19bc
@@ -1,6 +1,8 @@
|
||||
#include <Tactility/app/App.h>
|
||||
#include "Tactility/Paths.h"
|
||||
|
||||
#include <Tactility/app/App.h>
|
||||
#include <Tactility/app/AppManifestParsing.h>
|
||||
|
||||
#include <Tactility/MountPoints.h>
|
||||
#include <Tactility/app/AppManifest.h>
|
||||
#include <Tactility/app/AppRegistration.h>
|
||||
#include <Tactility/file/File.h>
|
||||
@@ -103,130 +105,105 @@ static bool untar(const std::string& tarPath, const std::string& destinationPath
|
||||
return success;
|
||||
}
|
||||
|
||||
bool findFirstMountedSdCardPath(std::string& path) {
|
||||
// const auto sdcards = hal::findDevices<hal::sdcard::SdCardDevice>(hal::Device::Type::SdCard);
|
||||
bool is_set = false;
|
||||
hal::findDevices<hal::sdcard::SdCardDevice>(hal::Device::Type::SdCard, [&is_set, &path](const auto& device) {
|
||||
if (device->isMounted()) {
|
||||
path = device->getMountPath();
|
||||
is_set = true;
|
||||
return false; // stop iterating
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return is_set;
|
||||
}
|
||||
|
||||
std::string getTempPath() {
|
||||
std::string root_path;
|
||||
if (!findFirstMountedSdCardPath(root_path)) {
|
||||
root_path = file::MOUNT_POINT_DATA;
|
||||
void cleanupInstallDirectory(const std::string& path) {
|
||||
const auto lock = file::getLock(path);
|
||||
lock->lock();
|
||||
if (!file::deleteRecursively(path)) {
|
||||
TT_LOG_W(TAG, "Failed to delete existing installation at %s", path.c_str());
|
||||
}
|
||||
return root_path + "/tmp";
|
||||
}
|
||||
|
||||
std::string getInstallPath() {
|
||||
std::string root_path;
|
||||
if (!findFirstMountedSdCardPath(root_path)) {
|
||||
root_path = file::MOUNT_POINT_DATA;
|
||||
}
|
||||
return root_path + "/apps";
|
||||
lock->unlock();
|
||||
}
|
||||
|
||||
bool install(const std::string& path) {
|
||||
// TODO: Make better: lock for each path type properly (source vs target)
|
||||
|
||||
// We lock and unlock frequently because SPI SD card devices share
|
||||
// the lock with the display. We don't want to lock the display for very long.
|
||||
|
||||
auto app_parent_path = getInstallPath();
|
||||
auto app_parent_path = getAppInstallPath();
|
||||
TT_LOG_I(TAG, "Installing app %s to %s", path.c_str(), app_parent_path.c_str());
|
||||
|
||||
auto lock = file::getLock(app_parent_path)->asScopedLock();
|
||||
auto target_path_lock = file::getLock(app_parent_path)->asScopedLock();
|
||||
|
||||
lock.lock();
|
||||
target_path_lock.lock();
|
||||
auto filename = file::getLastPathSegment(path);
|
||||
const std::string app_target_path = std::format("{}/{}", app_parent_path, filename);
|
||||
if (file::isDirectory(app_target_path) && !file::deleteRecursively(app_target_path)) {
|
||||
TT_LOG_W(TAG, "Failed to delete %s", app_target_path.c_str());
|
||||
}
|
||||
lock.unlock();
|
||||
target_path_lock.unlock();
|
||||
|
||||
lock.lock();
|
||||
target_path_lock.lock();
|
||||
if (!file::findOrCreateDirectory(app_target_path, 0777)) {
|
||||
TT_LOG_I(TAG, "Failed to create directory %s", app_target_path.c_str());
|
||||
return false;
|
||||
}
|
||||
lock.unlock();
|
||||
target_path_lock.unlock();
|
||||
|
||||
lock.lock();
|
||||
auto source_path_lock = file::getLock(path)->asScopedLock();
|
||||
target_path_lock.lock();
|
||||
source_path_lock.lock();
|
||||
TT_LOG_I(TAG, "Extracting app from %s to %s", path.c_str(), app_target_path.c_str());
|
||||
if (!untar(path, app_target_path)) {
|
||||
TT_LOG_E(TAG, "Failed to extract");
|
||||
return false;
|
||||
}
|
||||
lock.unlock();
|
||||
source_path_lock.unlock();
|
||||
target_path_lock.unlock();
|
||||
|
||||
lock.lock();
|
||||
target_path_lock.lock();
|
||||
auto manifest_path = app_target_path + "/manifest.properties";
|
||||
if (!file::isFile(manifest_path)) {
|
||||
TT_LOG_E(TAG, "Manifest not found at %s", manifest_path.c_str());
|
||||
cleanupInstallDirectory(app_target_path);
|
||||
return false;
|
||||
}
|
||||
lock.unlock();
|
||||
target_path_lock.unlock();
|
||||
|
||||
lock.lock();
|
||||
target_path_lock.lock();
|
||||
std::map<std::string, std::string> properties;
|
||||
if (!file::loadPropertiesFile(manifest_path, properties)) {
|
||||
TT_LOG_E(TAG, "Failed to load manifest at %s", manifest_path.c_str());
|
||||
cleanupInstallDirectory(app_target_path);
|
||||
return false;
|
||||
}
|
||||
lock.unlock();
|
||||
target_path_lock.unlock();
|
||||
|
||||
auto app_id_iterator = properties.find("[app]id");
|
||||
if (app_id_iterator == properties.end()) {
|
||||
TT_LOG_E(TAG, "Failed to find app id in manifest");
|
||||
AppManifest manifest;
|
||||
if (!parseManifest(properties, manifest)) {
|
||||
TT_LOG_W(TAG, "Invalid manifest");
|
||||
cleanupInstallDirectory(app_target_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto app_name_entry = properties.find("[app]name");
|
||||
if (app_name_entry == properties.end()) {
|
||||
TT_LOG_E(TAG, "Failed to find app name in manifest");
|
||||
return false;
|
||||
}
|
||||
|
||||
lock.lock();
|
||||
const std::string renamed_target_path = std::format("{}/{}", app_parent_path, app_id_iterator->second);
|
||||
target_path_lock.lock();
|
||||
const std::string renamed_target_path = std::format("{}/{}", app_parent_path, manifest.appId);
|
||||
if (file::isDirectory(renamed_target_path)) {
|
||||
if (!file::deleteRecursively(renamed_target_path)) {
|
||||
TT_LOG_W(TAG, "Failed to delete existing installation at %s", renamed_target_path.c_str());
|
||||
cleanupInstallDirectory(app_target_path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
target_path_lock.unlock();
|
||||
|
||||
lock.lock();
|
||||
target_path_lock.lock();
|
||||
if (rename(app_target_path.c_str(), renamed_target_path.c_str()) != 0) {
|
||||
TT_LOG_E(TAG, "Failed to rename %s to %s", app_target_path.c_str(), app_id_iterator->second.c_str());
|
||||
TT_LOG_E(TAG, "Failed to rename \"%s\" to \"%s\"", app_target_path.c_str(), manifest.appId.c_str());
|
||||
cleanupInstallDirectory(app_target_path);
|
||||
return false;
|
||||
}
|
||||
lock.unlock();
|
||||
target_path_lock.unlock();
|
||||
|
||||
addApp({
|
||||
.id = app_id_iterator->second,
|
||||
.name = app_name_entry->second,
|
||||
.category = Category::User,
|
||||
.location = Location::external(renamed_target_path)
|
||||
});
|
||||
manifest.appLocation = Location::external(renamed_target_path);
|
||||
|
||||
addApp(manifest);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uninstall(const std::string& appId) {
|
||||
TT_LOG_I(TAG, "Uninstalling app %s", appId.c_str());
|
||||
auto app_path = getInstallPath() + "/" + appId;
|
||||
return file::withLock<bool>(app_path, [&app_path, &appId]() {
|
||||
auto app_path = getAppInstallPath(appId);
|
||||
return file::withLock<bool>(app_path, [&app_path, &appId] {
|
||||
if (!file::isDirectory(app_path)) {
|
||||
TT_LOG_E(TAG, "App %s not found at ", app_path.c_str());
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user