blob: a386544390dba7923fc0cc9bdfb29c2873d431d9 [file]
// Copyright 2024 The Cobalt Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "starboard/android/shared/asset_manager.h"
#include <android/asset_manager.h>
#include <fcntl.h>
#include <ftw.h>
#include <linux/limits.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
#include "starboard/android/shared/file_internal.h"
#include "starboard/common/log.h"
#include "starboard/common/mutex.h"
#include "starboard/common/once.h"
#include "starboard/common/string.h"
#include "starboard/system.h"
namespace starboard {
namespace android {
namespace shared {
// static
SB_ONCE_INITIALIZE_FUNCTION(AssetManager, AssetManager::GetInstance);
AssetManager::AssetManager() {
const int kPathSize = PATH_MAX / 2;
char path[kPathSize] = {0};
SB_CHECK(SbSystemGetPath(kSbSystemPathTempDirectory, path, kPathSize))
<< "Unable to get system temp path for AssetManager.";
SB_CHECK(starboard::strlcat(path, "/asset_tmp", kPathSize) < kPathSize)
<< "Unable to construct temp path for AssetManager.";
tmp_root_ = path;
ClearTempDir();
}
uint64_t AssetManager::AcquireInternalFd() {
ScopedLock scoped_lock(mutex_);
do {
++internal_fd_;
} while (in_use_internal_fd_set_.count(internal_fd_) == 1);
in_use_internal_fd_set_.insert(internal_fd_);
return internal_fd_;
}
std::string AssetManager::TempFilepath(uint64_t internal_fd) const {
return tmp_root_ + "/" + std::to_string(internal_fd);
}
int AssetManager::Open(const char* path) {
if (!path) {
return -1;
}
AAsset* asset = OpenAndroidAsset(path);
if (!asset) {
SB_LOG(WARNING) << "Asset path not found within package: " << path;
return -1;
}
// Create temporary POSIX file for the asset
uint64_t internal_fd = AcquireInternalFd();
std::string filepath = TempFilepath(internal_fd);
int fd = open(filepath.c_str(), O_RDWR | O_TRUNC | O_CREAT);
if (fd < 0) {
mutex_.Acquire();
in_use_internal_fd_set_.erase(internal_fd);
mutex_.Release();
return -1;
}
// Copy contents of asset into temporary file and then seek to start of file.
const off_t size = AAsset_getLength(asset);
const void* const data = AAsset_getBuffer(asset);
if (write(fd, data, size) != size || lseek(fd, 0, SEEK_SET) != 0) {
SB_LOG(WARNING) << "Failed to write temporary file for asset: " << path;
mutex_.Acquire();
in_use_internal_fd_set_.erase(internal_fd);
mutex_.Release(); // Can't hold lock when calling close();
close(fd);
return -1;
}
AAsset_close(asset);
// Keep track of the internal fd so we can delete its file on close();
mutex_.Acquire();
fd_to_internal_fd_map_[fd] = internal_fd;
mutex_.Release();
return fd;
}
bool AssetManager::IsAssetFd(int fd) const {
ScopedLock scoped_lock(mutex_);
return fd_to_internal_fd_map_.count(fd) == 1;
}
int AssetManager::Close(int fd) {
mutex_.Acquire();
if (auto search = fd_to_internal_fd_map_.find(fd);
search != fd_to_internal_fd_map_.end()) {
uint64_t internal_fd = search->second;
fd_to_internal_fd_map_.erase(search);
in_use_internal_fd_set_.erase(internal_fd);
mutex_.Release(); // Can't hold lock when calling close();
int retval = close(fd);
std::string filepath = TempFilepath(internal_fd);
if (unlink(filepath.c_str()) != 0) {
SB_LOG(WARNING) << "Failed to delete temporary file: " << filepath;
}
return retval;
}
mutex_.Release();
return -1;
}
void AssetManager::ClearTempDir() {
auto callback = [](const char* child, const struct stat*, int file_type,
struct FTW*) -> int { return remove(child); };
nftw(tmp_root_.c_str(), callback, 32, FTW_DEPTH | FTW_PHYS);
mkdir(tmp_root_.c_str(), 0700);
}
} // namespace shared
} // namespace android
} // namespace starboard