blob: b2058430bac7ffb4775e53b58f7037b9c4df666b [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_utils.h"
#include "tuningfork/protobuf_util.h"
#include <cstdio>
#include <sys/stat.h>
#include <errno.h>
#include <fstream>
#include <sstream>
// Unfortunately we can't use std::filesystem until we move to C++17
#include <dirent.h>
#define LOG_TAG "TuningFork"
#include "Log.h"
#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include "jni_wrap.h"
namespace tuningfork {
std::string Base16(const std::vector<char>& bytes) {
static char hex_char[] = "0123456789abcdef";
std::string s(bytes.size()*2,' ');
auto q = s.begin();
for(char b: bytes) {
*q++ = hex_char[b>>4];
*q++ = hex_char[b&0xf];
}
return s;
}
namespace apk_utils {
class NativeAsset {
AAsset* asset;
public:
NativeAsset(const char* name) {
auto java_asset_manager = jni::AppContext().getAssets();
AAssetManager* mgr = AAssetManager_fromJava(jni::Env(), (jobject)java_asset_manager.obj_);
asset = AAssetManager_open(mgr, name, AASSET_MODE_BUFFER);
if (asset == nullptr) {
ALOGW("Can't find %s in APK", name);
}
}
NativeAsset(NativeAsset&& a) : asset(a.asset) {
a.asset = nullptr;
}
NativeAsset& operator=(NativeAsset&& a) {
asset = a.asset;
a.asset = nullptr;
return *this;
}
NativeAsset(const NativeAsset& a) = delete;
NativeAsset& operator=(const NativeAsset& a) = delete;
~NativeAsset() {
if (asset != nullptr) {
AAsset_close(asset);
}
}
bool IsValid() const {
return asset!=nullptr;
}
operator AAsset*() { return asset; }
};
bool GetAssetAsSerialization(const char* name,
ProtobufSerialization& out) {
NativeAsset asset(name);
if (!asset.IsValid()) return false;
uint64_t size = AAsset_getLength64(asset);
out.resize(size);
memcpy(const_cast<uint8_t*>(out.data()), AAsset_getBuffer(asset), size);
return true;
}
// Get the app's version code. Also fills packageNameStr with the package name
// if it is non-null.
int GetVersionCode(std::string* packageNameStr, uint32_t* gl_es_version) {
using namespace jni;
auto app_context = AppContext();
auto pm = app_context.getPackageManager();
CHECK_FOR_JNI_EXCEPTION_AND_RETURN(0);
std::string package_name = app_context.getPackageName().C();
CHECK_FOR_JNI_EXCEPTION_AND_RETURN(0);
auto package_info = pm.getPackageInfo(package_name, 0x0);
CHECK_FOR_JNI_EXCEPTION_AND_RETURN(0);
if (packageNameStr != nullptr) {
*packageNameStr = package_name;
}
auto code = package_info.versionCode();
CHECK_FOR_JNI_EXCEPTION_AND_RETURN(0);
if(gl_es_version != nullptr) {
auto features = pm.getSystemAvailableFeatures();
CHECK_FOR_JNI_EXCEPTION_AND_RETURN(0);
for (auto& f : features) {
if(f.name.empty()) {
if (f.reqGlEsVersion !=
android::content::pm::FeatureInfo::GL_ES_VERSION_UNDEFINED) {
*gl_es_version = f.reqGlEsVersion;
} else {
*gl_es_version = 1; // Lack of property means OpenGL ES version 1
}
}
}
ALOGI("OpenGL version %d.%d ", ((*gl_es_version) >> 16), ((*gl_es_version) & 0x0000ffff));
}
return code;
}
std::string GetSignature() {
using namespace jni;
auto app_context = AppContext();
auto pm = app_context.getPackageManager();
CHECK_FOR_JNI_EXCEPTION_AND_RETURN("");
auto package_name = app_context.getPackageName();
CHECK_FOR_JNI_EXCEPTION_AND_RETURN("");
auto package_info = pm.getPackageInfo(package_name.C(),
android::content::pm::PackageManager::GET_SIGNATURES);
CHECK_FOR_JNI_EXCEPTION_AND_RETURN("");
if (!package_info.valid()) return "";
auto sigs = package_info.signatures();
CHECK_FOR_JNI_EXCEPTION_AND_RETURN("");
if (sigs.size()==0) return "";
auto sig = sigs[0];
java::security::MessageDigest md("SHA1");
CHECK_FOR_JNI_EXCEPTION_AND_RETURN("");
auto padded_sig = md.digest(sigs[0]);
CHECK_FOR_JNI_EXCEPTION_AND_RETURN("");
return Base16(padded_sig);
}
bool GetDebuggable() {
using namespace jni;
if (!jni::IsValid()) return false;
auto app_context = AppContext();
auto pm = app_context.getPackageManager();
CHECK_FOR_JNI_EXCEPTION_AND_RETURN(false);
auto package_name = app_context.getPackageName();
CHECK_FOR_JNI_EXCEPTION_AND_RETURN(false);
auto package_info = pm.getPackageInfo(package_name.C(),0);
CHECK_FOR_JNI_EXCEPTION_AND_RETURN(false);
if (!package_info.valid()) return false;
auto application_info = package_info.applicationInfo();
CHECK_FOR_JNI_EXCEPTION_AND_RETURN(false);
if (!application_info.valid()) return false;
return application_info.flags()
& android::content::pm::ApplicationInfo::FLAG_DEBUGGABLE;
}
} // namespace apk_utils
namespace file_utils {
// Creates the directory if it does not exist. Returns true if the directory
// already existed or could be created.
bool CheckAndCreateDir(const std::string& path) {
ALOGV("CheckAndCreateDir:%s",path.c_str());
struct stat sb;
int32_t res = stat(path.c_str(), &sb);
if (0 == res && sb.st_mode & S_IFDIR) {
ALOGV("Directory %s already exists", path.c_str());
return true;
} else if (ENOENT == errno) {
ALOGI("Creating directory %s", path.c_str());
res = mkdir(path.c_str(), 0770);
if(res!=0)
ALOGW("Error creating directory %s: %d", path.c_str(), res);
return res==0;
}
return false;
}
bool FileExists(const std::string& fname) {
ALOGV("FileExists:%s",fname.c_str());
struct stat buffer;
return (stat(fname.c_str(), &buffer)==0);
}
std::string GetAppCacheDir() {
jni::String path = jni::AppContext().getCacheDir().getPath();
return path.C();
}
bool DeleteFile(const std::string& path) {
if (FileExists(path))
return remove(path.c_str())==0;
return true;
}
bool DeleteDir(const std::string& path) {
// TODO(willosborn): It would be much better to use std::filesystem::remove_all here
// if we were on C++17 or experimental/filesystem was supported on Android.
ALOGI("DeleteDir %s", path.c_str());
struct dirent* entry;
auto dir = opendir(path.c_str());
if (dir==nullptr)
return DeleteFile(path);
while ((entry = readdir(dir))) {
if (entry->d_name[0]!='\0' && entry->d_name[0]!='.')
DeleteDir(path + "/" + entry->d_name);
}
closedir(dir);
return true;
}
bool LoadBytesFromFile(std::string file_name, CProtobufSerialization* params) {
ALOGV("LoadBytesFromFile:%s",file_name.c_str());
std::ifstream f(file_name, std::ios::binary);
if (f.good()) {
f.seekg(0, std::ios::end);
params->size = f.tellg();
params->bytes = (uint8_t*)::malloc(params->size);
params->dealloc = CProtobufSerialization_Dealloc;
f.seekg(0, std::ios::beg);
f.read((char*)params->bytes, params->size);
return true;
}
return false;
}
bool SaveBytesToFile(std::string file_name, const CProtobufSerialization* params) {
ALOGV("SaveBytesToFile:%s",file_name.c_str());
std::ofstream save_file(file_name, std::ios::binary);
if (save_file.good()) {
save_file.write((const char*)params->bytes, params->size);
return true;
}
return false;
}
} // namespace file_utils
namespace json_utils {
using namespace json11;
std::string GetResourceName(const ExtraUploadInfo& request_info) {
std::stringstream str;
str << "applications/"<< request_info.apk_package_name<<"/apks/";
str << request_info.apk_version_code;
return str.str();
}
Json::object DeviceSpecJson(const ExtraUploadInfo& request_info) {
Json gles_version = Json::object {
{"major", static_cast<int>(request_info.gl_es_version>>16)},
{"minor", static_cast<int>(request_info.gl_es_version&0xffff)}};
std::vector<double> freqs(request_info.cpu_max_freq_hz.begin(),
request_info.cpu_max_freq_hz.end());
return Json::object {
{"fingerprint", request_info.build_fingerprint},
{"total_memory_bytes", static_cast<double>(request_info.total_memory_bytes)},
{"build_version", request_info.build_version_sdk},
{"gles_version", gles_version},
{"cpu_core_freqs_hz", freqs}};
}
} // namespace json_utils
std::string UniqueId() {
using namespace jni;
return java::util::UUID::randomUUID().toString().C();
}
} // namespace tuningfork