blob: 0401b506d489c669c242bc295e76653082e0c83a [file] [log] [blame]
// Copyright 2019 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/loader_app/installation_manager.h"
#include <set>
#include <string>
#include <vector>
#include "starboard/common/file.h"
#include "starboard/common/log.h"
#include "starboard/common/mutex.h"
#include "starboard/common/scoped_ptr.h"
#include "starboard/configuration_constants.h"
#include "starboard/directory.h"
#include "starboard/file.h"
#include "starboard/loader_app/installation_store.pb.h"
#include "starboard/loader_app/pending_restart.h"
#include "starboard/once.h"
#include "starboard/string.h"
namespace starboard {
namespace loader_app {
namespace installation_manager {
class InstallationManager {
public:
explicit InstallationManager(int max_num_installations,
const std::string& app_key);
int Initialize();
int GetAppKey(char* app_key, int app_key_length);
int GetMaxNumberInstallations();
int Reset();
int GetInstallationStatus(int installation_index);
int GetInstallationNumTriesLeft(int installation_index);
int RollForwardIfNeeded();
int RollForward(int installation_index);
int DecrementInstallationNumTries(int installation_index);
int RevertToSuccessfulInstallation();
int GetInstallationPath(int installation_index, char* path, int path_length);
int GetCurrentInstallationIndex();
int MarkInstallationSuccessful(int installation_index);
int SelectNewInstallationIndex();
int ResetInstallation(int installation_index);
int RequestRollForwardToInstallation(int installation_index);
private:
bool IsValidIndex(int i);
void ValidatePriorities();
int FindCurrentInstallationIndex();
void CreateInstallationStore();
std::string DumpInstallationSlots();
void LogLastSystemError(const char* msg);
void ShiftPrioritiesInRange(int high_priority,
int low_priority,
int shift_amount);
int RollForwardInternal(int installation);
bool InitInstallationStorePath();
bool LoadInstallationStore();
bool SaveInstallationStore();
bool CreateInstallationDirs();
bool CleanInstallationDirs();
bool GetInstallationPathInternal(int installation_index,
char* path,
int path_length);
cobalt::loader::InstallationStore installation_store_;
bool initialized_;
int current_installation_;
std::string app_key_;
std::string store_path_;
std::string storage_dir_;
std::string content_dir_;
const int max_num_installations_;
const int lowest_priority_;
const int highest_priority_;
};
InstallationManager::InstallationManager(int max_num_installations,
const std::string& app_key)
: initialized_(false),
current_installation_(-1),
app_key_(app_key),
max_num_installations_(max_num_installations),
lowest_priority_(max_num_installations_ - 1),
highest_priority_(0) {
SB_CHECK(max_num_installations_ >= 2);
SB_CHECK(!app_key.empty());
SB_LOG(INFO) << "InstallationManager: app_key=" << app_key_;
}
int InstallationManager::Initialize() {
if (initialized_) {
return IM_ERROR;
}
if (!InitInstallationStorePath()) {
SB_LOG(ERROR) << "Initialize: failed to init paths";
return IM_ERROR;
}
// If there is no existing store, create one.
if (!LoadInstallationStore()) {
CreateInstallationStore();
current_installation_ = FindCurrentInstallationIndex();
if (!IsValidIndex(current_installation_)) {
SB_LOG(ERROR) << "Initialize: Unable to find current installation"
<< current_installation_;
return IM_ERROR;
}
if (!CreateInstallationDirs()) {
SB_LOG(ERROR) << "Initialize: Unable to create installations dirs";
return IM_ERROR;
}
initialized_ = SaveInstallationStore();
} else {
current_installation_ = FindCurrentInstallationIndex();
if (!IsValidIndex(current_installation_)) {
SB_LOG(ERROR) << "Initialize: Unable to find current installation"
<< current_installation_;
return IM_ERROR;
}
initialized_ = true;
}
if (!initialized_) {
SB_LOG(ERROR) << "Initialize: failed";
}
SB_DLOG(INFO) << DumpInstallationSlots();
return initialized_ ? IM_SUCCESS : IM_ERROR;
}
int InstallationManager::GetAppKey(char* app_key, int app_key_length) {
SB_LOG(INFO) << "InstallationManager::GetAppKey";
if (!initialized_) {
SB_LOG(ERROR) << "GetAppKey: not initialized";
return IM_ERROR;
}
SbStringCopy(app_key, app_key_.c_str(), app_key_length);
return IM_SUCCESS;
}
int InstallationManager::GetMaxNumberInstallations() {
if (!initialized_) {
SB_LOG(ERROR) << "GetMaxNumberInstallations: not initialized";
return IM_ERROR;
}
return max_num_installations_;
}
int InstallationManager::Reset() {
CreateInstallationStore();
current_installation_ = FindCurrentInstallationIndex();
if (!IsValidIndex(current_installation_)) {
SB_LOG(ERROR) << "Reset: Unable to find current installation"
<< current_installation_;
return IM_ERROR;
}
if (!CleanInstallationDirs()) {
SB_LOG(ERROR) << "Reset: Unable to clean installations dirs";
return IM_ERROR;
}
if (!SaveInstallationStore()) {
return IM_ERROR;
}
return IM_SUCCESS;
}
void InstallationManager::CreateInstallationStore() {
installation_store_ = cobalt::loader::InstallationStore();
int priority = highest_priority_;
for (int i = 0; i < max_num_installations_; i++) {
installation_store_.add_installations();
installation_store_.mutable_installations(i)->set_is_successful(false);
installation_store_.mutable_installations(i)->set_num_tries_left(
IM_MAX_NUM_TRIES);
installation_store_.mutable_installations(i)->set_priority(priority++);
}
installation_store_.set_roll_forward_to_installation(-1);
// Mark the system image as successful.
installation_store_.mutable_installations(0)->set_is_successful(true);
}
std::string InstallationManager::DumpInstallationSlots() {
std::ostringstream out;
out << "size=";
const int kBufSize = 50;
char buf_num[kBufSize];
SbStringFormatF(buf_num, kBufSize, "%d",
installation_store_.installations_size());
out << buf_num;
out << " roll_forward_to_installation=";
SbStringFormatF(buf_num, kBufSize, "%d",
installation_store_.roll_forward_to_installation());
out << buf_num;
out << ";";
for (int i = 0; i < installation_store_.installations_size(); i++) {
out << " installation_";
SbStringFormatF(buf_num, kBufSize, "%d", i);
out << buf_num;
out << " is_successful=";
if (installation_store_.installations(i).is_successful()) {
out << "true";
} else {
out << "false";
}
out << " num_tries_left=";
SbStringFormatF(buf_num, kBufSize, "%d",
installation_store_.installations(i).num_tries_left());
out << buf_num;
out << " priority=";
SbStringFormatF(buf_num, kBufSize, "%d",
installation_store_.installations(i).priority());
out << buf_num;
out << ";";
}
return out.str();
}
int InstallationManager::GetInstallationStatus(int installation_index) {
if (!initialized_) {
SB_LOG(ERROR) << "GetInstallationStatus: not initialized";
return IM_INSTALLATION_STATUS_ERROR;
}
if (!IsValidIndex(installation_index)) {
SB_LOG(ERROR) << "GetInstallationStatus: invalid index: "
<< installation_index;
return IM_INSTALLATION_STATUS_ERROR;
}
return installation_store_.installations(installation_index).is_successful();
}
int InstallationManager::GetInstallationNumTriesLeft(int installation_index) {
if (!initialized_) {
SB_LOG(ERROR) << "GetInstallationNumTriesLeft: not initialized";
return IM_ERROR;
}
if (!IsValidIndex(installation_index)) {
SB_LOG(ERROR) << "GetInstallationNumTriesLeft: invalid index: "
<< installation_index;
return IM_ERROR;
}
return installation_store_.installations(installation_index).num_tries_left();
}
int InstallationManager::DecrementInstallationNumTries(int installation_index) {
if (!initialized_) {
SB_LOG(ERROR) << "DecrementInstallationNumTries: not initialized";
return IM_ERROR;
}
if (!IsValidIndex(installation_index)) {
SB_LOG(ERROR) << "DecrementInstallationNumTries: invalid index: "
<< installation_index;
return IM_ERROR;
}
int num_tries_left =
installation_store_.installations(installation_index).num_tries_left();
if (num_tries_left <= 0) {
return IM_ERROR;
}
installation_store_.mutable_installations(installation_index)
->set_num_tries_left(--num_tries_left);
if (!SaveInstallationStore()) {
SB_LOG(ERROR) << "DecrementInstallationNumTries: failed to save store";
return IM_ERROR;
}
return IM_SUCCESS;
}
//
// Revert to a previous successful installation and make it
// the highest priority. The current installation becomes the
// lowest priority.
//
// high [-] [x]
// [ ] [ ]
// [x] => [ ]
// low [ ] [-]
//
int InstallationManager::RevertToSuccessfulInstallation() {
if (!initialized_) {
SB_LOG(ERROR) << "RevertToSuccessfulInstallation: not initialized";
return IM_ERROR;
}
int fallback_priority = lowest_priority_;
int fallback_installation = -1;
SB_DLOG(INFO) << "RevertToSuccessfulInstallation: Start "
<< DumpInstallationSlots();
// Find the highest priority successful installation for fallback.
for (int i = 0; i < installation_store_.installations().size(); i++) {
int priority = installation_store_.installations(i).priority();
if (installation_store_.installations(i).is_successful() &&
priority > highest_priority_ && priority <= fallback_priority) {
fallback_priority = priority;
fallback_installation = i;
SB_DLOG(INFO) << "fallback_installation= " << fallback_installation;
}
}
if (fallback_installation == -1) {
SB_LOG(ERROR) << "RevertToSuccessfulInstallation: Unable to find fallback "
"installation";
return IM_ERROR;
}
// Shift up all the priorities below the fallback installation up.
ShiftPrioritiesInRange(fallback_priority, lowest_priority_,
-1 /* shift up -1 */);
// Disable current installation.
installation_store_.mutable_installations(current_installation_)
->set_priority(lowest_priority_);
// Move the current installation at the bottom.
installation_store_.mutable_installations(current_installation_)
->set_is_successful(false);
// Change current installation.
installation_store_.mutable_installations(fallback_installation)
->set_priority(highest_priority_);
current_installation_ = fallback_installation;
SB_DLOG(INFO) << "RevertToSuccessfulInstallation: End "
<< DumpInstallationSlots();
if (SaveInstallationStore()) {
return fallback_installation;
}
return IM_ERROR;
}
int InstallationManager::RollForward(int installation_index) {
if (!initialized_) {
SB_LOG(ERROR) << "RollForward: not initialized";
return IM_ERROR;
}
if (!IsValidIndex(installation_index)) {
SB_LOG(ERROR) << "RollForward: invalid installation=" << installation_index;
return IM_ERROR;
}
SB_DLOG(INFO) << "RollForward: installation=" << installation_index;
return RollForwardInternal(installation_index);
}
int InstallationManager::RollForwardIfNeeded() {
if (!initialized_) {
SB_LOG(ERROR) << "RollForwardIfNeeded: not initialized";
return IM_ERROR;
}
// Check if we need to roll_forward to new slot.
int new_installation = installation_store_.roll_forward_to_installation();
if (new_installation == -1) {
// No need to roll forward.
return IM_SUCCESS;
}
if (!IsValidIndex(new_installation)) {
SB_LOG(ERROR) << "RollForwardIfNeeded: invalid new_installation="
<< new_installation;
return IM_ERROR;
}
SB_DLOG(INFO) << "RollForwardIfNeeded: new_installation=" << new_installation;
return RollForwardInternal(new_installation);
}
//
// Roll forward to a new installation and make it
// the new highest priority installation.
//
// high [ ] [x]
// [ ] [ ]
// [x] => [ ]
// low [ ] [ ]
//
int InstallationManager::RollForwardInternal(int installation_index) {
// Save old priority.
int new_installation_old_prority =
installation_store_.installations(installation_index).priority();
SB_DLOG(INFO) << "RollForwardInternal: new_installation_old_priority="
<< new_installation_old_prority;
// Lower priorities of all jumped over installations.
ShiftPrioritiesInRange(highest_priority_, new_installation_old_prority,
1 /* shift down +1 */);
// The new installation will be set to the highest priority.
installation_store_.mutable_installations(installation_index)
->set_priority(highest_priority_);
// Reset the roll forward index.
installation_store_.set_roll_forward_to_installation(-1);
current_installation_ = installation_index;
SB_DLOG(INFO) << "RollForwardInternal: " << DumpInstallationSlots();
return SaveInstallationStore() ? IM_SUCCESS : IM_ERROR;
}
// Shift the priority in the inclusive range either up or down based
// on the |direction_up" flag.
void InstallationManager::ShiftPrioritiesInRange(int high_priority,
int low_priority,
int shift_amount) {
for (int i = 0; i < installation_store_.installations().size(); i++) {
int priority = installation_store_.installations(i).priority();
if (priority >= high_priority && priority <= low_priority) {
installation_store_.mutable_installations(i)->set_priority(priority +
shift_amount);
SB_DLOG(INFO) << "ShiftPrioritiesInRange i=" << i
<< " priority_new=" << priority;
}
}
}
int InstallationManager::GetInstallationPath(int installation_index,
char* path,
int path_length) {
if (!initialized_) {
SB_LOG(ERROR) << "GetInstallationPath: not initialized";
return IM_ERROR;
}
return GetInstallationPathInternal(installation_index, path, path_length)
? IM_SUCCESS
: IM_ERROR;
}
int InstallationManager::GetCurrentInstallationIndex() {
if (!initialized_) {
SB_LOG(ERROR) << "GetCurrentInstallationIndex: not initialized";
return IM_ERROR;
}
return current_installation_;
}
void InstallationManager::LogLastSystemError(const char* msg) {
const int kErrorMessageBufferSize = 256;
char msgbuf[kErrorMessageBufferSize];
SbSystemError error_code = SbSystemGetLastError();
if (SbSystemGetErrorString(error_code, msgbuf, kErrorMessageBufferSize) > 0) {
SB_LOG(ERROR) << msg << ": " << msgbuf;
}
}
int InstallationManager::FindCurrentInstallationIndex() {
int highest_priority_index = -1;
for (int i = 0; i < installation_store_.installations_size(); i++) {
if (highest_priority_ == installation_store_.installations(i).priority()) {
highest_priority_index = i;
break;
}
}
return highest_priority_index;
}
int InstallationManager::MarkInstallationSuccessful(int installation_index) {
if (!initialized_) {
SB_LOG(ERROR) << "MarkInstallationSuccessful: not initialized";
return IM_ERROR;
}
if (!IsValidIndex(installation_index)) {
SB_LOG(ERROR) << "MarkInstallationSuccessful: invalid index"
<< installation_index;
return IM_ERROR;
}
if (!installation_store_.installations(installation_index).is_successful()) {
installation_store_.mutable_installations(installation_index)
->set_is_successful(true);
return SaveInstallationStore() ? IM_SUCCESS : IM_ERROR;
}
return IM_SUCCESS;
}
int InstallationManager::SelectNewInstallationIndex() {
if (!initialized_) {
SB_LOG(ERROR) << "SelectNewInstallationIndex: not initialized";
return IM_ERROR;
}
int priority = highest_priority_;
int new_installation_index = -1;
// SLOT_0 is placed in |kSbSystemPathContentDirectory|, under the subdirectory
// 'app/cobalt', and is always the system image.
int start = 1;
// Find the lowest priority installation that we can use.
for (int i = start; i < installation_store_.installations().size(); i++) {
if (priority < installation_store_.installations(i).priority()) {
new_installation_index = i;
priority = installation_store_.installations(i).priority();
SB_DLOG(INFO) << "SelectNewInstallationIndex: lowest_priority_index= "
<< new_installation_index;
}
}
if (new_installation_index != -1) {
installation_store_.mutable_installations(new_installation_index)
->set_is_successful(false);
installation_store_.mutable_installations(new_installation_index)
->set_num_tries_left(IM_MAX_NUM_TRIES);
if (!SaveInstallationStore()) {
SB_LOG(ERROR) << "SelectNewInstallationIndex: failed to store";
return IM_ERROR;
}
SB_DLOG(INFO) << "SelectNewInstallationIndex: " << DumpInstallationSlots();
return new_installation_index;
}
return IM_ERROR;
}
int InstallationManager::ResetInstallation(int installation_index) {
if (!initialized_) {
SB_LOG(ERROR) << "ResetInstallation: not initialized";
return IM_ERROR;
}
// invalid index or the system image.
if (installation_index == -1 || installation_index == 0) {
return IM_ERROR;
}
installation_store_.mutable_installations(installation_index)
->set_is_successful(false);
installation_store_.mutable_installations(installation_index)
->set_num_tries_left(IM_MAX_NUM_TRIES);
if (!SaveInstallationStore()) {
SB_LOG(ERROR) << "ResetInstallation: failed to store";
return IM_ERROR;
}
SB_DLOG(INFO) << "ResetInstallation: " << DumpInstallationSlots();
return IM_SUCCESS;
}
int InstallationManager::RequestRollForwardToInstallation(
int installation_index) {
if (!initialized_) {
SB_LOG(ERROR) << "RequestRollForwardToInstallation: not initialized";
return IM_ERROR;
}
if (!IsValidIndex(installation_index)) {
SB_LOG(ERROR) << "RequestRollForwardToInstallation: invalid index"
<< installation_index;
return IM_ERROR;
}
installation_store_.set_roll_forward_to_installation(installation_index);
return SaveInstallationStore() ? IM_SUCCESS : IM_ERROR;
}
bool InstallationManager::SaveInstallationStore() {
ValidatePriorities();
if (IM_MAX_INSTALLATION_STORE_SIZE < installation_store_.ByteSize()) {
SB_LOG(ERROR) << "SaveInstallationStore: Data too large"
<< installation_store_.ByteSize();
return false;
}
const size_t buf_size = installation_store_.ByteSize();
std::vector<char> buf(buf_size, 0);
loader_app::SetPendingRestart(
installation_store_.roll_forward_to_installation() != -1);
installation_store_.SerializeToArray(buf.data(),
installation_store_.ByteSize());
#if SB_API_VERSION >= 12
if (!SbFileAtomicReplace(store_path_.c_str(), buf.data(),
installation_store_.ByteSize())) {
SB_LOG(ERROR)
<< "SaveInstallationStore: Failed to store installation store: "
<< store_path_;
return false;
}
SB_DLOG(INFO) << "SaveInstallationStore successful";
return true;
#else
SB_NOTREACHED()
<< "SbFileAtomicReplace is not available before starboard version 12";
return false;
#endif
}
bool InstallationManager::InitInstallationStorePath() {
std::vector<char> storage_dir(kSbFileMaxPath);
#if SB_API_VERSION >= 12
if (!SbSystemGetPath(kSbSystemPathStorageDirectory, storage_dir.data(),
kSbFileMaxPath)) {
SB_LOG(ERROR) << "InitInstallationStorePath: Failed to get "
"kSbSystemPathStorageDirectory";
return false;
}
#else
SB_NOTREACHED() << "InitInstallationStorePath: kSbSystemPathStorageDirectory "
"is not available before "
"starboard version "
<< 12;
return false;
#endif
storage_dir_ = storage_dir.data();
store_path_ = storage_dir.data();
store_path_ += kSbFileSepString;
store_path_ += IM_STORE_FILE_NAME_PREFIX;
store_path_ += app_key_;
store_path_ += IM_STORE_FILE_NAME_SUFFIX;
std::vector<char> content_dir(kSbFileMaxPath);
if (!SbSystemGetPath(kSbSystemPathContentDirectory, content_dir.data(),
kSbFileMaxPath)) {
return false;
}
content_dir_ = content_dir.data();
return true;
}
bool InstallationManager::IsValidIndex(int index) {
return index >= 0 && index < max_num_installations_;
}
void InstallationManager::ValidatePriorities() {
std::set<int> priorities;
for (int i = 0; i < max_num_installations_; i++) {
priorities.insert(installation_store_.installations(i).priority());
}
for (int i = 0; i < max_num_installations_; i++) {
SB_DCHECK(priorities.find(i) != priorities.end());
}
SB_DCHECK(priorities.size() == max_num_installations_);
}
bool InstallationManager::LoadInstallationStore() {
SbFile file;
SB_LOG(INFO) << "StorePath=" << store_path_;
file = SbFileOpen(store_path_.c_str(), kSbFileOpenOnly | kSbFileRead, NULL,
NULL);
if (!file) {
SB_LOG(WARNING) << "Failed to open file: " << store_path_;
return false;
}
char buf[IM_MAX_INSTALLATION_STORE_SIZE];
int count = SbFileReadAll(file, buf, IM_MAX_INSTALLATION_STORE_SIZE);
SB_DLOG(INFO) << "SbFileReadAll: count=" << count;
if (count == -1) {
LogLastSystemError("SbFileReadAll failed");
return false;
}
if (!installation_store_.ParseFromArray(buf, count)) {
SB_LOG(ERROR) << "LoadInstallationStore: Unable to parse storage";
return false;
}
SbFileClose(file);
return true;
}
bool InstallationManager::GetInstallationPathInternal(int installation_index,
char* path,
int path_length) {
if (!IsValidIndex(installation_index)) {
SB_LOG(ERROR) << "GetInstallationPath: invalid index" << installation_index;
return false;
}
if (!path) {
SB_LOG(ERROR) << "GetInstallationPath: path is null";
return false;
}
// SLOT_0 is placed in |kSbSystemPathContentDirectory|, under the subdirectory
// 'app/cobalt'.
if (installation_index == 0) {
SbStringFormatF(path, path_length, "%s%s%s%s%s", content_dir_.c_str(),
kSbFileSepString, "app", kSbFileSepString, "cobalt");
} else {
SbStringFormatF(path, path_length, "%s%s%s%d", storage_dir_.c_str(),
kSbFileSepString, "installation_", installation_index);
}
return true;
}
bool InstallationManager::CreateInstallationDirs() {
std::vector<char> path(kSbFileMaxPath);
for (int i = 0; i < max_num_installations_; i++) {
// SLOT_0 is placed in |kSbSystemPathContentDirectory|, under the
// subdirectory 'app/cobalt'.
if (i == 0) {
continue;
}
if (!GetInstallationPathInternal(i, path.data(), kSbFileMaxPath)) {
return false;
}
if (!SbDirectoryCreate(path.data())) {
return false;
}
}
return true;
}
bool InstallationManager::CleanInstallationDirs() {
std::vector<char> path(kSbFileMaxPath);
// The index 0 slot is under the content directory.
for (int i = 1; i < max_num_installations_; i++) {
if (!GetInstallationPathInternal(i, path.data(), kSbFileMaxPath)) {
return false;
}
if (!SbFileDeleteRecursive(path.data(), true)) {
return false;
}
}
return true;
}
} // namespace installation_manager
} // namespace loader_app
} // namespace starboard
#ifdef __cplusplus
extern "C" {
#endif
starboard::scoped_ptr<
starboard::loader_app::installation_manager::InstallationManager>
g_installation_manager_;
// Global Installation Manager Mutex.
SB_ONCE_INITIALIZE_FUNCTION(starboard::Mutex, GetImMutex);
int ImInitialize(int max_num_installations, const char* app_key) {
starboard::ScopedLock lock(*GetImMutex());
if (g_installation_manager_.get() == NULL) {
g_installation_manager_.reset(
new starboard::loader_app::installation_manager::InstallationManager(
max_num_installations, app_key));
}
return g_installation_manager_->Initialize();
}
int ImGetAppKey(char* app_key, int app_key_length) {
starboard::ScopedLock lock(*GetImMutex());
return g_installation_manager_->GetAppKey(app_key, app_key_length);
}
int ImGetMaxNumberInstallations() {
starboard::ScopedLock lock(*GetImMutex());
return g_installation_manager_->GetMaxNumberInstallations();
}
void ImUninitialize() {
starboard::ScopedLock lock(*GetImMutex());
g_installation_manager_.reset(NULL);
}
int ImReset() {
starboard::ScopedLock lock(*GetImMutex());
return g_installation_manager_->Reset();
}
int ImGetInstallationStatus(int installation_index) {
starboard::ScopedLock lock(*GetImMutex());
return g_installation_manager_->GetInstallationStatus(installation_index);
}
int ImGetInstallationNumTriesLeft(int installation_index) {
starboard::ScopedLock lock(*GetImMutex());
return g_installation_manager_->GetInstallationNumTriesLeft(
installation_index);
}
int ImDecrementInstallationNumTries(int installation_index) {
starboard::ScopedLock lock(*GetImMutex());
return g_installation_manager_->DecrementInstallationNumTries(
installation_index);
}
int ImGetCurrentInstallationIndex() {
starboard::ScopedLock lock(*GetImMutex());
return g_installation_manager_->GetCurrentInstallationIndex();
}
int ImResetInstallation(int installation_index) {
starboard::ScopedLock lock(*GetImMutex());
return g_installation_manager_->ResetInstallation(installation_index);
}
int ImSelectNewInstallationIndex() {
starboard::ScopedLock lock(*GetImMutex());
return g_installation_manager_->SelectNewInstallationIndex();
}
int ImGetInstallationPath(int installation_index, char* path, int path_length) {
starboard::ScopedLock lock(*GetImMutex());
return g_installation_manager_->GetInstallationPath(installation_index, path,
path_length);
}
int ImMarkInstallationSuccessful(int installation_index) {
starboard::ScopedLock lock(*GetImMutex());
return g_installation_manager_->MarkInstallationSuccessful(
installation_index);
}
int ImRollForwardIfNeeded() {
starboard::ScopedLock lock(*GetImMutex());
return g_installation_manager_->RollForwardIfNeeded();
}
int ImRollForward(int installation_index) {
starboard::ScopedLock lock(*GetImMutex());
return g_installation_manager_->RollForward(installation_index);
}
int ImRevertToSuccessfulInstallation() {
starboard::ScopedLock lock(*GetImMutex());
return g_installation_manager_->RevertToSuccessfulInstallation();
}
int ImRequestRollForwardToInstallation(int installation_index) {
starboard::ScopedLock lock(*GetImMutex());
return g_installation_manager_->RequestRollForwardToInstallation(
installation_index);
}
#ifdef __cplusplus
} // extern "C"
#endif