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:
committed by
Jeff Tinker
parent
6dad75e395
commit
5bb2e55318
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user