Move Watchdog Timer to L3 Init Only

Merge from Widevine repo of http://go/wvgerrit/31340

This CL moves the oemcrypto watchdog timer so that it only watches the
L3 initialization.  This will allow L1 initialization to take more
than 5 seconds if it needs to.

TEST: oemcrypto unit tests, Media GTS tests, Play Movies
b/64069544

Change-Id: I7826e4d72eda52ae8b2c9f8b3ac360fb42cbb115
This commit is contained in:
Fred Gylys-Colwell
2017-08-08 13:26:50 -07:00
committed by Jeff Tinker
parent 6dad75e395
commit 5bb2e55318

View File

@@ -376,6 +376,162 @@ void clear_cache_function(void *page, size_t len) {
#endif
}
// The WatchDog looks after a worker thread that is trying to initialize L3.
// Once in a rare while, the L3 init does not finish and eats up CPU cycles.
// If that happens, the watchdog thread will give up and return an error.
class WatchDog {
public:
// Created by main thread.
WatchDog() {
pthread_mutex_init(&mutex_, NULL);
pthread_cond_init(&condition_, NULL);
status_ = OEMCrypto_SUCCESS;
gave_up_ = false;
}
// Deleted by either thread.
~WatchDog() {
pthread_cond_destroy(&condition_);
}
// Starts worker thread.
void StartThread() {
running_ = true;
if(pthread_create(&thread_, NULL, RunWatchDog, this)) {
LOGE("Could not create watch dog thread.");
status_ = OEMCrypto_ERROR_INIT_FAILED;
running_ = false;
return;
}
}
// Function called by new worker thread in pthread_create.
static void *RunWatchDog(void *watcher) {
WatchDog* dog = reinterpret_cast<WatchDog *>(watcher);
dog->DoInit();
dog->SignalDoneAndCleanUp();
return NULL;
}
// Called by worker thread.
void DoInit() {
std::string base_path;
wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
&base_path);
status_ = Level3_Initialize(clear_cache_function,
base_path.c_str());
}
std::string FailureFilename() {
std::string path;
if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
&path)) {
LOGW("WatchDog::FailureFilename: Unable to get base path");
return "/data/l3_failure_file";
}
path += "l3_failure_file";
return path;
}
// Check to see if the failure file was created before that last abort.
void CheckForPreviousFailure(wvcdm::metrics::CryptoMetrics* metrics) {
wvcdm::FileSystem file_system;
std::string filename = FailureFilename();
if (!file_system.Exists(filename)) return;
wvcdm::File* file = file_system.Open(filename, file_system.kReadOnly);
if (file) {
uint32_t flag = 0;
ssize_t size = sizeof(flag);
ssize_t size_read = file->Read(reinterpret_cast<char*>(&flag), size);
file->Close();
file_system.Remove(filename);
if (size == size_read && flag) {
LOGE("Previous L3 Init failed.");
if (metrics == nullptr) return;
M_RECORD(
metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_L3_INITIALIZATION_FAILED);
}
}
}
// Save the failure file before we abort.
void SaveFailureInformation() {
wvcdm::FileSystem file_system;
std::string filename = FailureFilename();
LOGD("failure filename = %s", filename.c_str());
wvcdm::File* file = file_system.Open(
filename, file_system.kCreate | file_system.kTruncate);
if (!file) {
LOGE("Could not create file %s", filename.c_str());
return;
}
uint32_t flag = 0x6261640a; // bad
ssize_t size = sizeof(flag);
ssize_t size_written = file->Write(reinterpret_cast<char*>(&flag), size);
file->Close();
if (size != size_written) {
LOGE("Wrote %d bytes, not %d, to file %s", size_written, size,
filename.c_str());
} else {
LOGE("I wrote %d to %s", size_written, filename.c_str());
}
}
// Called by worker thread after DoInit has finshed.
void SignalDoneAndCleanUp() {
pthread_mutex_lock(&mutex_);
running_ = false;
pthread_cond_signal(&condition_);
// If the main thread gave up, it won't delete this, so we must.
bool should_delete = gave_up_;
pthread_mutex_unlock(&mutex_);
// https://isocpp.org/wiki/faq/freestore-mgmt#delete-this
if (should_delete) delete this;
}
// Called by main thread to wait for worker thread.
OEMCryptoResult WaitForStatusAndCleanUp() {
pthread_mutex_lock(&mutex_);
struct timespec time_to_giveup;
clock_gettime(CLOCK_REALTIME, &time_to_giveup);
time_to_giveup.tv_sec += 5; // wait 5 seconds.
if (running_) {
pthread_cond_timedwait(&condition_, &mutex_, &time_to_giveup);
}
if (running_) {
gave_up_ = true;
status_ = OEMCrypto_ERROR_INIT_FAILED;
LOGE("XXX WATCH DOG ERROR XXX");
SaveFailureInformation();
// This is controversial. The argument for an abort here is that if we
// do not abort, we will suck all the life out of the user's battery. By
// saving information to the file system, above, we can still track
// metrics.
abort();
}
// If we gave up waiting for init thread, we should not delete the mutex
// out from under it.
bool should_delete = !gave_up_;
OEMCryptoResult status = status_;
pthread_mutex_unlock(&mutex_);
if (should_delete) delete this;
return status;
}
OEMCryptoResult status() { return status_; }
private:
OEMCryptoResult status_;
pthread_t thread_;
pthread_mutex_t mutex_;
pthread_cond_t condition_;
bool running_;
bool gave_up_;
};
struct LevelSession {
FunctionPointers* fcn;
OEMCrypto_SESSION session;
@@ -418,21 +574,29 @@ class Adapter {
~Adapter() {
}
OEMCryptoResult Initialize(wvcdm::metrics::CryptoMetrics* metrics) {
if (metrics == nullptr) {
return OEMCrypto_ERROR_INIT_FAILED;
}
OEMCryptoResult Initialize() {
/*
* To avoid changing the function signature and function contract - declare
* a one-off metrics group to collect detailed information about how
* oemcrypto was intialized.
*
* TODO(blueeyes): Refactor this to allow Initialize to provide the
* details to the caller or to use the metrics instance provided by
* the caller.
*/
wvcdm::metrics::CryptoMetrics metrics;
level1_ = FunctionPointers(); // start with all null pointers.
level3_ = FunctionPointers(); // start with all null pointers.
LoadLevel3();
std::string base_path;
wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
&base_path);
OEMCryptoResult result = Level3_Initialize(clear_cache_function,
base_path.c_str());
WatchDog *watcher = new WatchDog();
watcher->CheckForPreviousFailure(&metrics);
watcher->StartThread();
OEMCryptoResult result = watcher->WaitForStatusAndCleanUp();
if (Level3_IsInApp()) {
M_RECORD(
metrics,
&metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_IN_APP);
@@ -441,7 +605,7 @@ class Adapter {
if (force_level3()) {
LOGW("Test code. User requested falling back to L3");
M_RECORD(
metrics,
&metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_FORCING_L3);
@@ -451,7 +615,7 @@ class Adapter {
if (!wvcdm::Properties::GetOEMCryptoPath(&library_name)) {
LOGW("L1 library not specified. Falling back to L3");
M_RECORD(
metrics,
&metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_NO_L1_LIBRARY_PATH);
@@ -462,13 +626,13 @@ class Adapter {
LOGW("Could not load %s. Falling back to L3. %s", library_name.c_str(),
dlerror());
M_RECORD(
metrics,
&metrics,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_USING_L3_L1_OPEN_FAILED);
return result;
}
if (LoadLevel1(metrics)) {
if (LoadLevel1(&metrics)) {
LOGD("OEMCrypto_Initialize Level 1 success. I will use level 1.");
} else {
level1_ = FunctionPointers(); // revert to all null pointers.
@@ -841,173 +1005,6 @@ class Adapter {
static Adapter* kAdapter = 0;
void *RunWatchDog(void *watcher);
// The WatchDog looks after a worker thread that is trying to initialize L3.
// Once in a rare while, the L3 init does not finish and eats up CPU cycles.
// If that happens, the watchdog thread will give up and return an error.
class WatchDog {
public:
// Created by main thread.
WatchDog() {
pthread_mutex_init(&mutex_, NULL);
pthread_cond_init(&condition_, NULL);
status_ = OEMCrypto_SUCCESS;
gave_up_ = false;
}
// Deleted by either thread.
~WatchDog() {
pthread_cond_destroy(&condition_);
}
// Starts worker thread.
void StartThread() {
running_ = true;
if(pthread_create(&thread_, NULL, RunWatchDog, this)) {
LOGE("Could not create watch dog thread.");
status_ = OEMCrypto_ERROR_INIT_FAILED;
running_ = false;
return;
}
}
// Called by worker thread.
void DoInit() {
if (kAdapter) {
kAdapter->Terminate();
delete kAdapter;
}
kAdapter = new Adapter();
status_ = kAdapter->Initialize(&metrics_);
}
std::string FailureFilename() {
std::string path;
if (!wvcdm::Properties::GetDeviceFilesBasePath(wvcdm::kSecurityLevelL3,
&path)) {
LOGW("DeviceFiles::StoreFileRaw: Unable to get base path");
return "/data/l3_failure_file";
}
path += "l3_failure_file";
return path;
}
// Check to see if the failure file was created before that last abort.
void CheckForPreviousFailure() {
wvcdm::FileSystem file_system;
std::string filename = FailureFilename();
if (!file_system.Exists(filename)) return;
wvcdm::File* file = file_system.Open(filename, file_system.kReadOnly);
if (file) {
uint32_t flag = 0;
ssize_t size = sizeof(flag);
ssize_t size_read = file->Read(reinterpret_cast<char*>(&flag), size);
file->Close();
file_system.Remove(filename);
if (size == size_read && flag) {
LOGE("Previous L3 Init failed.");
M_RECORD(
&metrics_,
oemcrypto_initialization_mode_,
NO_TIME,
wvcdm::metrics::OEMCrypto_INITIALIZED_L3_INITIALIZATION_FAILED);
}
}
}
// Save the failure file before we abort.
void SaveFailureInformation() {
wvcdm::FileSystem file_system;
std::string filename = FailureFilename();
LOGD("failure filename = %s", filename.c_str());
wvcdm::File* file = file_system.Open(
filename, file_system.kCreate | file_system.kTruncate);
if (!file) {
LOGE("Could not create file %s", filename.c_str());
return;
}
uint32_t flag = 0x6261640a; // bad
ssize_t size = sizeof(flag);
ssize_t size_written = file->Write(reinterpret_cast<char*>(&flag), size);
file->Close();
if (size != size_written) {
LOGE("Wrote %d bytes, not %d, to file %s", size_written, size,
filename.c_str());
} else {
LOGE("I wrote %d to %s", size_written, filename.c_str());
}
}
// Called by worker thread after DoInit has finshed.
void SignalDoneAndCleanUp() {
pthread_mutex_lock(&mutex_);
running_ = false;
pthread_cond_signal(&condition_);
// If the main thread gave up, it won't delete this, so we must.
bool should_delete = gave_up_;
pthread_mutex_unlock(&mutex_);
// https://isocpp.org/wiki/faq/freestore-mgmt#delete-this
if (should_delete) delete this;
}
// Called by main thread to wait for worker thread.
OEMCryptoResult WaitForStatusAndCleanUp() {
pthread_mutex_lock(&mutex_);
struct timespec time_to_giveup;
clock_gettime(CLOCK_REALTIME, &time_to_giveup);
time_to_giveup.tv_sec += 5; // wait 5 seconds.
if (running_) {
pthread_cond_timedwait(&condition_, &mutex_, &time_to_giveup);
}
if (running_) {
gave_up_ = true;
status_ = OEMCrypto_ERROR_INIT_FAILED;
LOGE("XXX WATCH DOG ERROR XXX");
SaveFailureInformation();
// This is controversial. The argument for an abort here is that if we
// do not abort, we will suck all the life out of the user's battery. By
// saving information to the file system, above, we can still track
// metrics.
abort();
}
// If we gave up waiting for init thread, we should not delete the mutex
// out from under it.
bool should_delete = !gave_up_;
OEMCryptoResult status = status_;
pthread_mutex_unlock(&mutex_);
if (should_delete) delete this;
return status;
}
OEMCryptoResult status() { return status_; }
private:
OEMCryptoResult status_;
pthread_t thread_;
pthread_mutex_t mutex_;
pthread_cond_t condition_;
bool running_;
bool gave_up_;
// A Metrics Group for the different initialization outcomes. Since one
// outcome is a watchdog timeout and abort on the PREVIOUS initialization,
// we put the group here, owned by the watchdog timer.
//
// TODO(blueeyes): Refactor this to allow Initialize to provide the
// details to the caller or to use the metrics instance provided by
// the caller.
wvcdm::metrics::CryptoMetrics metrics_;
};
// Function called by new worker thread in pthread_create.
void *RunWatchDog(void *watcher) {
WatchDog* dog = reinterpret_cast<WatchDog *>(watcher);
dog->DoInit();
dog->SignalDoneAndCleanUp();
return NULL;
}
} // namespace
namespace wvcdm {
@@ -1228,10 +1225,12 @@ OEMCryptoResult OEMCrypto_CreateOldUsageEntry(
} // namespace wvcdm
extern "C" OEMCryptoResult OEMCrypto_Initialize(void) {
WatchDog *watcher = new WatchDog();
watcher->CheckForPreviousFailure();
watcher->StartThread();
return watcher->WaitForStatusAndCleanUp();
if (kAdapter) {
kAdapter->Terminate();
delete kAdapter;
}
kAdapter = new Adapter();
return kAdapter->Initialize();
}
extern "C" OEMCryptoResult OEMCrypto_Terminate(void) {