blob: fc9ea35bdc287e6246d75491164f40e896479b64 [file] [log] [blame]
/*
* Copyright 2019 The Android Open Source Project
*
* 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 "tuningfork/tuningfork_extra.h"
#include "tuningfork/protobuf_util.h"
#include "tuningfork_internal.h"
#include "tuningfork_utils.h"
#include "jni_helper.h"
#include <cinttypes>
#include <dlfcn.h>
#include <memory>
#include <vector>
#include <cstdlib>
#include <sstream>
#include <thread>
#include <fstream>
#include <mutex>
#include <chrono>
#define LOG_TAG "TuningFork"
#include "Log.h"
#include <android/asset_manager_jni.h>
#include <jni.h>
namespace tuningfork {
// Get the name of the tuning fork save file. Returns true if the directory
// for the file exists and false on error.
bool GetSavedFileName(std::string& name) {
// Create tuningfork/version folder if it doesn't exist
std::stringstream tf_path_str;
tf_path_str << file_utils::GetAppCacheDir() << "/tuningfork";
if (!file_utils::CheckAndCreateDir(tf_path_str.str())) {
return false;
}
tf_path_str << "/V" << apk_utils::GetVersionCode();
if (!file_utils::CheckAndCreateDir(tf_path_str.str())) {
return false;
}
tf_path_str << "/saved_fp.bin";
name = tf_path_str.str();
return true;
}
// Get a previously save fidelity param serialization.
bool GetSavedFidelityParams(ProtobufSerialization& params) {
std::string save_filename;
if (GetSavedFileName(save_filename)) {
CProtobufSerialization c_params;
if (file_utils::LoadBytesFromFile(save_filename, &c_params)) {
ALOGI("Loaded fps from %s (%u bytes)", save_filename.c_str(), c_params.size);
params = ToProtobufSerialization(c_params);
CProtobufSerialization_Free(&c_params);
return true;
}
ALOGI("Couldn't load fps from %s", save_filename.c_str());
}
return false;
}
// Save fidelity params to the save file.
bool SaveFidelityParams(const ProtobufSerialization& params) {
std::string save_filename;
if (GetSavedFileName(save_filename)) {
std::ofstream save_file(save_filename, std::ios::binary);
if (save_file.good()) {
save_file.write(reinterpret_cast<const char*>(params.data()), params.size());
ALOGI("Saved fps to %s (%zu bytes)", save_filename.c_str(), params.size());
return true;
}
ALOGI("Couldn't save fps to %s", save_filename.c_str());
}
return false;
}
// Check if we have saved fidelity params.
bool SavedFidelityParamsFileExists() {
std::string save_filename;
if (GetSavedFileName(save_filename)) {
return file_utils::FileExists(save_filename);
}
return false;
}
static bool s_kill_thread = false;
static std::unique_ptr<std::thread> s_fp_thread;
// Download FPs on a separate thread
TFErrorCode StartFidelityParamDownloadThread(const ProtobufSerialization& default_params,
ProtoCallback fidelity_params_callback,
int initialTimeoutMs, int ultimateTimeoutMs) {
if (fidelity_params_callback == nullptr) return TFERROR_BAD_PARAMETER;
static std::mutex threadMutex;
std::lock_guard<std::mutex> lock(threadMutex);
if (s_fp_thread.get() && s_fp_thread->joinable()) {
ALOGW("Fidelity param download thread already started");
return TFERROR_DOWNLOAD_THREAD_ALREADY_STARTED;
}
s_kill_thread = false;
s_fp_thread = std::make_unique<std::thread>([=]() {
ProtobufSerialization params;
auto waitTime = std::chrono::milliseconds(initialTimeoutMs);
bool first_time = true;
while (!s_kill_thread) {
auto startTime = std::chrono::steady_clock::now();
auto err = GetFidelityParameters(default_params,
params, waitTime.count());
if (err==TFERROR_OK) {
ALOGI("Got fidelity params from server");
if (jni::IsValid())
SaveFidelityParams(params);
CProtobufSerialization cpbs;
ToCProtobufSerialization(params, cpbs);
if (fidelity_params_callback) {
fidelity_params_callback(&cpbs);
}
CProtobufSerialization_Free(&cpbs);
break;
} else {
ALOGI("Could not get fidelity params from server : err = %d", err);
if (first_time) {
CProtobufSerialization cpbs;
ToCProtobufSerialization(default_params, cpbs);
if (fidelity_params_callback)
fidelity_params_callback(&cpbs);
CProtobufSerialization_Free(&cpbs);
first_time = false;
}
// Wait if the call returned earlier than expected
auto dt = std::chrono::steady_clock::now() - startTime;
if(waitTime>dt) std::this_thread::sleep_for(waitTime - dt);
if (waitTime.count() > ultimateTimeoutMs) {
ALOGW("Not waiting any longer for fidelity params");
break;
}
waitTime *= 2; // back off
}
}
if (jni::IsValid())
jni::DetachThread();
});
return TFERROR_OK;
}
// Load fidelity params from assets/tuningfork/<filename>
// Ownership of serializations is passed to the caller: call
// CProtobufSerialization_Free to deallocate any memory.
TFErrorCode FindFidelityParamsInApk(const std::string& filename,
ProtobufSerialization& fp) {
std::stringstream full_filename;
full_filename << "tuningfork/" << filename;
if (!apk_utils::GetAssetAsSerialization(full_filename.str().c_str(), fp)) {
ALOGE("Can't find %s", full_filename.str().c_str());
return TFERROR_NO_FIDELITY_PARAMS;
}
ALOGV("Found %s", full_filename.str().c_str());
return TFERROR_OK;
}
std::unique_ptr<ProtobufSerialization> GetTrainingParams(const Settings& settings) {
std::unique_ptr<ProtobufSerialization> training_params;
auto cpbs = settings.c_settings.training_fidelity_params;
if (cpbs != nullptr) {
training_params = std::make_unique<ProtobufSerialization>(ToProtobufSerialization(*cpbs));
}
return training_params;
}
TFErrorCode GetDefaultsFromAPKAndDownloadFPs(const Settings& settings) {
ProtobufSerialization default_params;
// Use the saved params as default, if they exist
if (SavedFidelityParamsFileExists()) {
ALOGI("Using saved default params");
GetSavedFidelityParams(default_params);
} else {
// Use the training mode params if they are present
auto training_params = GetTrainingParams(settings);
if (training_params.get() != nullptr) {
default_params = *training_params;
}
else {
// Try to get the parameters from file.
if (settings.default_fidelity_parameters_filename.empty())
return TFERROR_INVALID_DEFAULT_FIDELITY_PARAMS;
auto err = FindFidelityParamsInApk(
settings.default_fidelity_parameters_filename.c_str(),
default_params);
if (err!=TFERROR_OK)
return err;
ALOGI("Using file %s for default params",
settings.default_fidelity_parameters_filename.c_str());
}
}
auto training_params = GetTrainingParams(settings);
StartFidelityParamDownloadThread(default_params,
settings.c_settings.fidelity_params_callback,
settings.initial_request_timeout_ms,
settings.ultimate_request_timeout_ms);
return TFERROR_OK;
}
TFErrorCode KillDownloadThreads() {
if (s_fp_thread.get() && s_fp_thread->joinable()) {
s_kill_thread = true;
s_fp_thread->join();
s_fp_thread.reset();
return TFERROR_OK;
}
return TFERROR_TUNINGFORK_NOT_INITIALIZED;
}
} // namespace tuningfork
extern "C" {
using namespace tuningfork;
// Download FPs on a separate thread
TFErrorCode TuningFork_startFidelityParamDownloadThread(
const CProtobufSerialization* c_default_params,
ProtoCallback fidelity_params_callback) {
if (c_default_params==nullptr) return TFERROR_BAD_PARAMETER;
if (fidelity_params_callback==nullptr) return TFERROR_BAD_PARAMETER;
const Settings* settings = GetSettings();
if (settings == nullptr) return TFERROR_TUNINGFORK_NOT_INITIALIZED;
return StartFidelityParamDownloadThread(ToProtobufSerialization(*c_default_params),
fidelity_params_callback,
settings->initial_request_timeout_ms,
settings->ultimate_request_timeout_ms);
}
// Load fidelity params from assets/tuningfork/<filename>
// Ownership of serializations is passed to the caller: call
// CProtobufSerialization_Free to deallocate any memory.
TFErrorCode TuningFork_findFidelityParamsInApk(JNIEnv* env, jobject context,
const char* filename,
CProtobufSerialization* c_fps) {
if (c_fps==nullptr) return TFERROR_BAD_PARAMETER;
jni::Init(env, context);
ProtobufSerialization fps;
auto err = FindFidelityParamsInApk(filename, fps);
if (err != TFERROR_OK) return err;
ToCProtobufSerialization(fps, *c_fps);
return TFERROR_OK;
}
TFErrorCode TuningFork_setUploadCallback(UploadCallback cbk) {
return tuningfork::SetUploadCallback(cbk);
}
TFErrorCode TuningFork_saveOrDeleteFidelityParamsFile(JNIEnv* env, jobject context,
const CProtobufSerialization* fps) {
jni::Init(env, context);
if(fps) {
if (SaveFidelityParams(ToProtobufSerialization(*fps)))
return TFERROR_OK;
} else {
std::string save_filename;
if (GetSavedFileName(save_filename)) {
if (file_utils::DeleteFile(save_filename))
return TFERROR_OK;
}
}
return TFERROR_COULDNT_SAVE_OR_DELETE_FPS;
}
} // extern "C"