Skip to content

Commit

Permalink
Cherry pick PR #3151: Add Android asset manager. (#3189)
Browse files Browse the repository at this point in the history
Refer to the original PR: #3151

b/302715109

Change-Id: I3f22470821c1c77695d7c740c66ae227944e87c5

---------

Co-authored-by: Yijia Zhang <[email protected]>
Co-authored-by: Yijia Zhang <[email protected]>
  • Loading branch information
3 people authored May 31, 2024
1 parent 197ef16 commit 6470b5a
Show file tree
Hide file tree
Showing 6 changed files with 261 additions and 7 deletions.
3 changes: 3 additions & 0 deletions starboard/android/shared/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ static_library("starboard_platform") {
"android_media_session_client.cc",
"application_android.cc",
"application_android.h",
"asset_manager.cc",
"asset_manager.h",
"atomic_public.h",
"audio_decoder.cc",
"audio_decoder.h",
Expand Down Expand Up @@ -378,6 +380,7 @@ static_library("starboard_platform") {
"player_set_max_video_input_size.cc",
"player_set_max_video_input_size.h",
"player_set_playback_rate.cc",
"posix_emu/file.cc",
"posix_emu/pthread.cc",
"posix_emu/stat.cc",
"sanitizer_options.cc",
Expand Down
138 changes: 138 additions & 0 deletions starboard/android/shared/asset_manager.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// 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
54 changes: 54 additions & 0 deletions starboard/android/shared/asset_manager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// 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.

#ifndef STARBOARD_ANDROID_SHARED_ASSET_MANAGER_H_
#define STARBOARD_ANDROID_SHARED_ASSET_MANAGER_H_

#include <map>
#include <set>
#include <string>

#include "starboard/common/mutex.h"

namespace starboard {
namespace android {
namespace shared {

// This class handles opening/closing Android asset files as POSIX filehandles.
class AssetManager {
public:
static AssetManager* GetInstance();
int Open(const char* path);
int Close(int fd);
bool IsAssetFd(int fd) const;

private:
AssetManager();
~AssetManager() { ClearTempDir(); }
uint64_t AcquireInternalFd();
std::string TempFilepath(uint64_t internal_fd) const;
void ClearTempDir();

std::string tmp_root_;
mutable Mutex mutex_;
uint64_t internal_fd_ = 0; // Guarded by |mutex_|.
std::set<uint64_t> in_use_internal_fd_set_; // Guarded by |mutex_|.
std::map<int, uint64_t> fd_to_internal_fd_map_; // Guarded by |mutex_|.
};

} // namespace shared
} // namespace android
} // namespace starboard

#endif // STARBOARD_ANDROID_SHARED_ASSET_MANAGER_H_
6 changes: 5 additions & 1 deletion starboard/android/shared/platform_configuration/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,11 @@ config("platform_configuration") {
"-Wl,--wrap=eglSwapBuffers",
]
if (!is_native_target_build) {
ldflags += [ "-Wl,--wrap=stat" ]
ldflags += [
"-Wl,--wrap=close",
"-Wl,--wrap=open",
"-Wl,--wrap=stat",
]
}
}

Expand Down
61 changes: 61 additions & 0 deletions starboard/android/shared/posix_emu/file.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// 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 <fcntl.h>

#include <vector>

#include "starboard/android/shared/asset_manager.h"
#include "starboard/android/shared/file_internal.h"
#include "starboard/common/log.h"
#include "starboard/configuration_constants.h"
#include "starboard/directory.h"
#include "starboard/log.h"

using starboard::android::shared::AssetManager;
using starboard::android::shared::IsAndroidAssetPath;
using starboard::android::shared::OpenAndroidAsset;

// ///////////////////////////////////////////////////////////////////////////////
// // Implementations below exposed externally in pure C for emulation.
// ///////////////////////////////////////////////////////////////////////////////

extern "C" {
int __real_close(int fildes);
int __real_open(const char* path, int oflag, ...);

int __wrap_close(int fildes) {
AssetManager* asset_manager = AssetManager::GetInstance();
if (asset_manager->IsAssetFd(fildes)) {
return asset_manager->Close(fildes);
}
return __real_close(fildes);
}

int __wrap_open(const char* path, int oflag, ...) {
if (!IsAndroidAssetPath(path)) {
va_list args;
va_start(args, oflag);
int fd;
if (oflag & O_CREAT) {
mode_t mode = va_arg(args, int);
return __real_open(path, oflag, mode);
} else {
return __real_open(path, oflag);
}
}
return AssetManager::GetInstance()->Open(path);
}

} // extern "C"
6 changes: 0 additions & 6 deletions starboard/android/shared/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,6 @@
'PosixDirectoryOpenTest.SunnyDayStaticContent',
'PosixFileGetPathInfoTest.WorksOnStaticContentDirectories',

# These POSIX tests should be disabled until asset manager starboard
# extension is implemented.
'PosixFileGetInfoTest.WorksOnStaticContentFiles',
'PosixFileReadTest/*.ReadStaticContent',
'PosixFileSeekTest.FromEndInStaticContentWorks',

# These tests are disabled due to not receiving the kEndOfStream
# player state update within the specified timeout.
'SbPlayerGetAudioConfigurationTests/SbPlayerGetAudioConfigurationTest.NoInput/*',
Expand Down

0 comments on commit 6470b5a

Please sign in to comment.