diff --git a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp index 231ca0da..c780e4bf 100644 --- a/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp +++ b/libwvdrmengine/cdm/core/src/oemcrypto_adapter_dynamic.cpp @@ -40,7 +40,7 @@ using wvcdm::kLevel3; namespace { static const size_t kMaxGenericEncryptChunkSize = 100*1024; -OEMCryptoResult kOemCryptoResultVendorSpecificError1 = +const OEMCryptoResult kOemCryptoResultVendorSpecificError1 = static_cast(10008); typedef struct { @@ -847,6 +847,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(&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(&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(watcher); + dog->DoInit(); + dog->SignalDoneAndCleanUp(); + return NULL; +} + } // namespace namespace wvcdm { @@ -1067,12 +1220,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) {