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:
committed by
Jeff Tinker
parent
0bbbf7d84e
commit
e20e909cbd
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user