| // 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" |
| #if !SB_IS(EVERGREEN_COMPATIBLE_LITE) |
| #include "starboard/loader_app/pending_restart.h" |
| #endif // !SB_IS(EVERGREEN_COMPATIBLE_LITE) |
| #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_priority = |
| installation_store_.installations(installation_index).priority(); |
| |
| SB_DLOG(INFO) << "RollForwardInternal: new_installation_old_priority=" |
| << new_installation_old_priority; |
| |
| // Lower priorities of all jumped over installations. |
| ShiftPrioritiesInRange(highest_priority_, new_installation_old_priority, |
| 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); |
| |
| int result = installation_store_.roll_forward_to_installation(); |
| #if !SB_IS(EVERGREEN_COMPATIBLE_LITE) |
| loader_app::SetPendingRestart(result != -1); |
| #endif |
| |
| 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 |