Add watch dog timer to OEMCrypto L3

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

This code adds a watchdog timer to the oemcrypto initialization.  If
initialization does not finish within 5 seconds, the process will
abort.

Testing: see code in patch 1.  Watch dog was forced while using Play
Movies.  The busy spinner spun for at least 5 seconds, but Play Movies
was able to restart itself.

b/62106796

Change-Id: Ib59f5bc4a484eff1dc386e07a4b198ecb713c69b
This commit is contained in:
Fred Gylys-Colwell
2017-07-13 18:56:47 -07:00
committed by Jeff Tinker
parent 0bbbf7d84e
commit e20e909cbd

View File

@@ -41,7 +41,7 @@ using wvcdm::kLevel3;
namespace {
static const size_t kMaxGenericEncryptChunkSize = 100*1024;
OEMCryptoResult kOemCryptoResultVendorSpecificError1 =
const OEMCryptoResult kOemCryptoResultVendorSpecificError1 =
static_cast<OEMCryptoResult>(10008);
typedef struct {
@@ -844,6 +844,159 @@ 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();
}
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.");
}
}
}
// 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 += 2; // wait 2 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_;
};
// 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 {
@@ -1064,12 +1217,10 @@ OEMCryptoResult OEMCrypto_CreateOldUsageEntry(
} // namespace wvcdm
extern "C" OEMCryptoResult OEMCrypto_Initialize(void) {
if (kAdapter) {
kAdapter->Terminate();
delete kAdapter;
}
kAdapter = new Adapter();
return kAdapter->Initialize();
WatchDog *watcher = new WatchDog();
watcher->CheckForPreviousFailure();
watcher->StartThread();
return watcher->WaitForStatusAndCleanUp();
}
extern "C" OEMCryptoResult OEMCrypto_Terminate(void) {