diff options
author | android-build-prod (mdb) <android-build-team-robot@google.com> | 2019-10-08 00:24:15 +0000 |
---|---|---|
committer | android-build-prod (mdb) <android-build-team-robot@google.com> | 2019-10-08 00:24:15 +0000 |
commit | 38f20d8958b8c6e8ef8852dc88ad4ecbabe0e154 (patch) | |
tree | d2f64e96175f97f8c381714f3b8b1a0a096a9102 | |
parent | a038ad1bb3d1bb79e75a3432c60ac6e766192a3a (diff) | |
parent | d83e225d40bd38b1cb3e0eb1bf984e8d23b89278 (diff) | |
download | android-sdk-release.tar.gz |
Snap for 5925869 from d83e225d40bd38b1cb3e0eb1bf984e8d23b89278 to sdk-releaseplatform-tools-29.0.6platform-tools-29.0.5sdk-release
Change-Id: I94889b8c6577e77cf3c187b28b7a695f53015273
-rw-r--r-- | .checkpatch.conf | 7 | ||||
-rw-r--r-- | .clang-format | 11 | ||||
-rw-r--r-- | OWNERS | 1 | ||||
-rw-r--r-- | citadel/citadeld/Android.bp | 1 | ||||
-rw-r--r-- | citadel/citadeld/aidl/android/hardware/citadel/ICitadeld.aidl | 3 | ||||
-rw-r--r-- | citadel/citadeld/main.cpp | 329 | ||||
-rw-r--r-- | citadel/libnos_datagram/citadel.c | 2 | ||||
-rw-r--r-- | citadel/validation/citadel_validation_tool.cpp | 2 | ||||
-rw-r--r-- | hals/Android.bp | 1 | ||||
-rw-r--r-- | hals/keymaster/Android.bp | 2 | ||||
-rw-r--r-- | hals/keymaster/buffer.cpp | 1 | ||||
-rw-r--r-- | hals/keymaster/citadel/Android.bp | 1 | ||||
-rw-r--r-- | hals/keymaster/proto_utils.cpp | 4 |
13 files changed, 326 insertions, 39 deletions
diff --git a/.checkpatch.conf b/.checkpatch.conf new file mode 100644 index 0000000..e8e9db6 --- /dev/null +++ b/.checkpatch.conf @@ -0,0 +1,7 @@ +# Not Linux, so don't expect a Linux tree. +--no-tree + +# Ignore aspects we don't follow here. +--ignore FILE_PATH_CHANGES +--ignore GIT_COMMIT_ID +--ignore SPLIT_STRING diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..7453c4d --- /dev/null +++ b/.clang-format @@ -0,0 +1,11 @@ +BasedOnStyle: Google +AccessModifierOffset: -2 +AllowShortFunctionsOnASingleLine: Inline +ColumnLimit: 80 +CommentPragmas: NOLINT:.* +DerivePointerAlignment: false +IndentWidth: 4 +ContinuationIndentWidth: 8 +PointerAlignment: Left +TabWidth: 4 +UseTab: Never @@ -3,3 +3,4 @@ # Please update this list if you find better owner candidates. ngm@google.com ascull@google.com +wfrichar@google.com diff --git a/citadel/citadeld/Android.bp b/citadel/citadeld/Android.bp index 41a58bd..1b3a6e7 100644 --- a/citadel/citadeld/Android.bp +++ b/citadel/citadeld/Android.bp @@ -32,6 +32,7 @@ cc_defaults { "libbinder", "libnos", "libutils", + "//hardware/google/pixel:pixelpowerstats_provider_aidl_interface-cpp", ], } diff --git a/citadel/citadeld/aidl/android/hardware/citadel/ICitadeld.aidl b/citadel/citadeld/aidl/android/hardware/citadel/ICitadeld.aidl index 2fc1a3b..ee59982 100644 --- a/citadel/citadeld/aidl/android/hardware/citadel/ICitadeld.aidl +++ b/citadel/citadeld/aidl/android/hardware/citadel/ICitadeld.aidl @@ -30,4 +30,7 @@ interface ICitadeld { /** Reset Citadel by pulling the reset line. */ boolean reset(); + + /** Get cached low-power stats */ + void getCachedStats(out byte[] response); } diff --git a/citadel/citadeld/main.cpp b/citadel/citadeld/main.cpp index 2afbdb6..d0d345f 100644 --- a/citadel/citadeld/main.cpp +++ b/citadel/citadeld/main.cpp @@ -14,27 +14,40 @@ * limitations under the License. */ +#include <algorithm> +#include <chrono> +#include <condition_variable> +#include <functional> +#include <future> +#include <iomanip> #include <limits> -#include <thread> #include <mutex> +#include <thread> #include <android-base/logging.h> +#include <binder/IBinder.h> #include <binder/IPCThreadState.h> #include <binder/IServiceManager.h> -#include <nos/device.h> +#include <app_nugget.h> +#include <citadel_events.h> #include <nos/NuggetClient.h> +#include <nos/device.h> #include <android/hardware/citadel/BnCitadeld.h> -using ::android::OK; +#include <android/vendor/powerstats/BnPixelPowerStatsCallback.h> +#include <android/vendor/powerstats/BnPixelPowerStatsProvider.h> +#include <android/vendor/powerstats/StateResidencyData.h> + using ::android::defaultServiceManager; -using ::android::sp; -using ::android::status_t; using ::android::IPCThreadState; using ::android::IServiceManager; +using ::android::OK; using ::android::ProcessState; - +using ::android::sp; +using ::android::status_t; +using ::android::wp; using ::android::binder::Status; using ::nos::NuggetClient; @@ -42,32 +55,170 @@ using ::nos::NuggetClient; using ::android::hardware::citadel::BnCitadeld; using ::android::hardware::citadel::ICitadeld; +using android::IBinder; +using android::vendor::powerstats::BnPixelPowerStatsCallback; +using android::vendor::powerstats::IPixelPowerStatsProvider; +using android::vendor::powerstats::StateResidencyData; + namespace { +using namespace std::chrono_literals; + +// This attaches a timer to a function call. Call .schedule() to start the +// timer, and the function will be called (once) after the time has elapsed. If +// you call .schedule() again before that happens, it just restarts the timer. +// There's no way to cancel the function call after it's scheduled; you can only +// postpone it. +class DeferredCallback { + public: + DeferredCallback(std::chrono::milliseconds delay, std::function<void()> fn) + : _armed(false), + _delay(delay), + _func(fn), + _waiter_thread(std::bind(&DeferredCallback::waiter_task, this)) {} + ~DeferredCallback() {} + + // [re]start the timer for the delayed call + void schedule() { + std::unique_lock<std::mutex> _lock(_cv_mutex); + _armed = true; + _cv.notify_one(); + } + + private: + void waiter_task(void) { + std::unique_lock<std::mutex> _lock(_cv_mutex); + while (true) { + if (!_armed) { + _cv.wait(_lock); + } + auto timeout = std::chrono::steady_clock::now() + _delay; + if (_cv.wait_until(_lock, timeout) == std::cv_status::timeout) { + _func(); + _armed = false; + } + } + } + + bool _armed; + const std::chrono::milliseconds _delay; + const std::function<void()> _func; + std::thread _waiter_thread; + std::mutex _cv_mutex; + std::condition_variable _cv; +}; + +// This provides a Binder interface for the powerstats service to fetch our +// power stats info from. This is a secondary function of citadeld. Failures +// here must not block or delay AP/Citadel communication. +class StatsDelegate : public BnPixelPowerStatsCallback, + public IBinder::DeathRecipient { + public: + StatsDelegate(std::function<Status(std::vector<StateResidencyData>*)> fn) + : func_(fn) {} + + // methods from BnPixelPowerStatsCallback + virtual Status getStats(std::vector<StateResidencyData>* stats) override { + return func_(stats); + } + + // methods from IBinder::DeathRecipient + virtual IBinder* onAsBinder() override { return this; } + virtual void binderDied(const wp<IBinder>& who) override { + LOG(INFO) << "powerstats service died"; + const sp<IBinder>& service = who.promote(); + if (service != nullptr) { + service->unlinkToDeath(this); + } + sp<IBinder> powerstats_service = WaitForPowerStatsService(); + registerWithPowerStats(powerstats_service); + } + + // post-creation init (Binder calls inside constructor are troublesome) + void registerWithPowerStats(sp<IBinder>& powerstats_service) { + sp<IPixelPowerStatsProvider> powerstats_provider = + android::interface_cast<IPixelPowerStatsProvider>( + powerstats_service); + + LOG(INFO) << "signing up for a notification if powerstats dies"; + auto ret = asBinder(powerstats_provider) + ->linkToDeath(this, 0u /* cookie */); + if (ret != android::OK) { + LOG(ERROR) << "linkToDeath() returned " << ret + << " - we will NOT be notified on powerstats death"; + } + + LOG(INFO) << "registering our callback with powerstats service"; + Status status = powerstats_provider->registerCallback("Citadel", this); + if (!status.isOk()) { + LOG(ERROR) << "failed to register callback: " << status.toString8(); + } + } + + // static helper function + static sp<IBinder> WaitForPowerStatsService() { + LOG(INFO) << "waiting for powerstats service to appear"; + sp<IBinder> svc; + while (true) { + svc = defaultServiceManager()->checkService( + android::String16("power.stats-vendor")); + if (svc != nullptr) { + LOG(INFO) << "A wild powerstats service has appeared!"; + return svc; + } + sleep(1); + } + } + + // Creates a new StatsDelegate only after a powerstats service becomes + // available for it to register with. + static sp<StatsDelegate> MakeOne( + std::function<Status(std::vector<StateResidencyData>*)> fn) { + sp<IBinder> powerstats_service = + StatsDelegate::WaitForPowerStatsService(); + sp<StatsDelegate> sd = new StatsDelegate(fn); + sd->registerWithPowerStats(powerstats_service); + return sd; + } + + private: + const std::function<Status(std::vector<StateResidencyData>*)> func_; +}; + class CitadelProxy : public BnCitadeld { -public: - CitadelProxy(NuggetClient& client) : _client{client} {} + public: + CitadelProxy(NuggetClient& client) + : _client{client}, + _event_thread(std::bind(&CitadelProxy::dispatchEvents, this)), + _stats_collection(500ms, std::bind(&CitadelProxy::cacheStats, this)) { + } ~CitadelProxy() override = default; - Status callApp(const int32_t _appId, const int32_t _arg, const std::vector<uint8_t>& request, - std::vector<uint8_t>* const response, int32_t* const _aidl_return) override { - // AIDL doesn't support integers less than 32-bit so validate it before casting + // methods from BnCitadeld + + Status callApp(const int32_t _appId, const int32_t _arg, + const std::vector<uint8_t>& request, + std::vector<uint8_t>* const response, + int32_t* const _aidl_return) override { + // AIDL doesn't support integers less than 32-bit so validate it before + // casting if (_appId < 0 || _appId > kMaxAppId) { LOG(ERROR) << "App ID " << _appId << " is outside the app ID range"; return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); } if (_arg < 0 || _arg > std::numeric_limits<uint16_t>::max()) { - LOG(ERROR) << "Argument " << _arg << " is outside the unsigned 16-bit range"; + LOG(ERROR) << "Argument " << _arg + << " is outside the unsigned 16-bit range"; return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); } const uint8_t appId = static_cast<uint32_t>(_appId); - const uint16_t arg = static_cast<uint16_t>(_arg); + const uint16_t arg = static_cast<uint16_t>(_arg); uint32_t* const appStatus = reinterpret_cast<uint32_t*>(_aidl_return); + *appStatus = lockedCallApp(appId, arg, request, response); + + _stats_collection.schedule(); - // Make the call to the app while holding the lock for that app - std::unique_lock<std::mutex> lock(_appLocks[appId]); - *appStatus = _client.CallApp(appId, arg, request, response); return Status::ok(); } @@ -79,28 +230,133 @@ public: return Status::ok(); } + Status getCachedStats(std::vector<uint8_t>* const response) override { + std::unique_lock<std::mutex> lock(_stats_mutex); + response->resize(sizeof(_stats)); + memcpy(response->data(), &_stats, sizeof(_stats)); + return Status::ok(); + } + + // Interaction with the powerstats service is handled by the StatsDelegate + // class, but its getStats() method calls this to access our cached stats. + Status onGetStats(std::vector<StateResidencyData>* stats) { + std::unique_lock<std::mutex> lock(_stats_mutex); + + StateResidencyData data1; + data1.state = "Last-Reset"; + data1.totalTimeInStateMs = _stats.time_since_hard_reset / 1000; + data1.totalStateEntryCount = _stats.hard_reset_count; + data1.lastEntryTimestampMs = 0; + stats->emplace_back(data1); + + StateResidencyData data2; + data2.state = "Active"; + data2.totalTimeInStateMs = _stats.time_spent_awake / 1000; + data2.totalStateEntryCount = _stats.wake_count; + data2.lastEntryTimestampMs = _stats.time_at_last_wake / 1000; + stats->emplace_back(data2); + + StateResidencyData data3; + data3.state = "Deep-Sleep"; + data3.totalTimeInStateMs = _stats.time_spent_in_deep_sleep / 1000; + data3.totalStateEntryCount = _stats.deep_sleep_count; + data3.lastEntryTimestampMs = _stats.time_at_last_deep_sleep / 1000; + stats->emplace_back(data3); + + return Status::ok(); + } + private: static constexpr auto kMaxAppId = std::numeric_limits<uint8_t>::max(); NuggetClient& _client; + std::thread _event_thread; std::mutex _appLocks[kMaxAppId + 1]; -}; + struct nugget_app_low_power_stats _stats; + DeferredCallback _stats_collection; + std::mutex _stats_mutex; -[[noreturn]] void CitadelEventDispatcher(const nos_device& device) { - LOG(INFO) << "Event dispatcher startup."; - while(1) { - if (device.ops.wait_for_interrupt(device.ctx, -1) > 0) { - LOG(INFO) << "Citadel has dispatched an event"; - } else { - LOG(INFO) << "Citadel did something unexpected"; + // Make the call to the app while holding the lock for that app + uint32_t lockedCallApp(uint32_t appId, uint16_t arg, + const std::vector<uint8_t>& request, + std::vector<uint8_t>* response) { + std::unique_lock<std::mutex> lock(_appLocks[appId]); + return _client.CallApp(appId, arg, request, response); + } + + void cacheStats(void) { + std::vector<uint8_t> buffer; + + buffer.reserve(sizeof(_stats)); + uint32_t rv = lockedCallApp(APP_ID_NUGGET, + NUGGET_PARAM_GET_LOW_POWER_STATS, buffer, + &buffer); + if (rv == APP_SUCCESS) { + std::unique_lock<std::mutex> lock(_stats_mutex); + memcpy(&_stats, buffer.data(), + std::min(sizeof(_stats), buffer.size())); } + } + + [[noreturn]] void dispatchEvents(void) { + LOG(INFO) << "Event dispatcher startup."; + + const nos_device& device = *_client.Device(); + + while (true) { - // This is a placeholder for the message handling that gives citadel a - // chance to deassert CTLD_AP_IRQ, so this doesn't spam the logs. - // TODO(b/62713383) Replace this with the code to contact citadel. - sleep(1); + const int wait_rv = device.ops.wait_for_interrupt(device.ctx, -1); + if (wait_rv <= 0) { + LOG(WARNING) << "device.ops.wait_for_interrupt: " << wait_rv; + continue; + } + + // CTDL_AP_IRQ is asserted, fetch all the event_records from Citadel + while (true) { + struct event_record evt; + std::vector<uint8_t> buffer; + buffer.reserve(sizeof(evt)); + const uint32_t rv = lockedCallApp(APP_ID_NUGGET, + NUGGET_PARAM_GET_EVENT_RECORD, + buffer, &buffer); + if (rv != APP_SUCCESS) { + LOG(WARNING) << "failed to fetch event_record: " << rv; + break; + } + + if (buffer.size() == 0) { + // Success but no data means we've fetched them all + break; + } + + // TODO(b/34946126): Do something more than just log it + memcpy(&evt, buffer.data(), sizeof(evt)); + const uint64_t secs = evt.uptime_usecs / 1000000UL; + const uint64_t usecs = evt.uptime_usecs - (secs * 1000000UL); + LOG(INFO) << std::setfill('0') << std::internal + << "event_record " << evt.reset_count << "/" + << secs << "." << std::setw(6) << usecs + << " " << evt.id + << std::hex + << " 0x" << std::setw(8) << evt.u.raw.w[0] + << " 0x" << std::setw(8) << evt.u.raw.w[1] + << " 0x" << std::setw(8) << evt.u.raw.w[2]; + } + + // TODO: Add a more intelligent back-off (and other action?) here + // + // When Citadel indicates that it has event_records for us, we fetch + // one at a time without delay until we've gotten them all (and then + // wait a bit to give it time to deassert CTDL_AP_IRQ). + // + // OTOH, if Citadel is just constantly asserting CTDL_AP_IRQ but + // doesn't actually have any events for us, then a) that's probably + // a bug, and b) we shouldn't spin madly here just querying it over + // and over. + sleep(1); + } } -} +}; } // namespace @@ -122,14 +378,23 @@ int main() { const status_t status = defaultServiceManager()->addService(ICitadeld::descriptor, proxy); if (status != OK) { LOG(FATAL) << "Failed to register citadeld as a service (status " << status << ")"; + return 1; } - // Handle interrupts triggered by Citadel and dispatch any events to - // registered listeners. - std::thread event_dispatcher(CitadelEventDispatcher, *citadel.Device()); + // We'll create a StatsDelegate object to talk to the powerstats service, + // but it will need a function to access the stats we've cached in the + // CitadelProxy object. + std::function<Status(std::vector<StateResidencyData>*)> fn = + std::bind(&CitadelProxy::onGetStats, proxy, std::placeholders::_1); + + // Use a separate thread to wait for the powerstats service to appear, so + // the Citadel proxy can start working ASAP. + std::future<sp<StatsDelegate>> sd = + std::async(std::launch::async, StatsDelegate::MakeOne, fn); // Start handling binder requests with multiple threads ProcessState::self()->startThreadPool(); IPCThreadState::self()->joinThreadPool(); + return 0; } diff --git a/citadel/libnos_datagram/citadel.c b/citadel/libnos_datagram/citadel.c index f2ba9ba..026224d 100644 --- a/citadel/libnos_datagram/citadel.c +++ b/citadel/libnos_datagram/citadel.c @@ -34,7 +34,7 @@ #include <unistd.h> /*****************************************************************************/ -/* TODO: #include <linux/citadel.h> */ +/* Ideally, this should be in <linux/citadel.h> */ #define CITADEL_IOC_MAGIC 'c' struct citadel_ioc_tpm_datagram { __u64 buf; diff --git a/citadel/validation/citadel_validation_tool.cpp b/citadel/validation/citadel_validation_tool.cpp index 39c0338..5fddb44 100644 --- a/citadel/validation/citadel_validation_tool.cpp +++ b/citadel/validation/citadel_validation_tool.cpp @@ -156,7 +156,6 @@ bool CyclesSinceBoot(NuggetClientInterface & client, uint32_t* cycles) { * The current implementation of the test writes random vales to registers and * reads them back. This lets us check the correct values were sent across the * channel. - * TODO(b/65067435): Replace with less intrusive calls. */ int CmdStressSpi(NuggetClientInterface& client, char** params) { std::random_device rd; @@ -211,7 +210,6 @@ int CmdStressSpi(NuggetClientInterface& client, char** params) { /* * The current implementation directly reads some registers and checks they * contain valid values. - * TODO(b/65067435): Replace with less intrusive calls. */ int CmdHealthCheck(NuggetClientInterface& client) { int ret = EXIT_SUCCESS; diff --git a/hals/Android.bp b/hals/Android.bp index 4b004a7..2ab832a 100644 --- a/hals/Android.bp +++ b/hals/Android.bp @@ -22,7 +22,6 @@ cc_defaults { shared_libs: [ "libbase", "libhidlbase", - "libhidltransport", "libnos", "libnosprotos", "libutils", diff --git a/hals/keymaster/Android.bp b/hals/keymaster/Android.bp index b956e20..7d75562 100644 --- a/hals/keymaster/Android.bp +++ b/hals/keymaster/Android.bp @@ -37,7 +37,7 @@ cc_library { "libcrypto", "libprotobuf-cpp-full", "nos_app_keymaster", - "libhidltransport", + "libhidlbase", ], export_include_dirs: ["include"], } diff --git a/hals/keymaster/buffer.cpp b/hals/keymaster/buffer.cpp index 35a7d4f..89feadc 100644 --- a/hals/keymaster/buffer.cpp +++ b/hals/keymaster/buffer.cpp @@ -56,6 +56,7 @@ public: case Algorithm::EC: case Algorithm::HMAC: _blockSize = 0; + break; default: break; } diff --git a/hals/keymaster/citadel/Android.bp b/hals/keymaster/citadel/Android.bp index c62578a..f9a7dec 100644 --- a/hals/keymaster/citadel/Android.bp +++ b/hals/keymaster/citadel/Android.bp @@ -24,7 +24,6 @@ cc_binary { shared_libs: [ "libbase", "libhidlbase", - "libhidltransport", "libnos", "libnosprotos", "libutils", diff --git a/hals/keymaster/proto_utils.cpp b/hals/keymaster/proto_utils.cpp index a1fbfb0..03c7b43 100644 --- a/hals/keymaster/proto_utils.cpp +++ b/hals/keymaster/proto_utils.cpp @@ -789,9 +789,11 @@ ErrorCode hidl_params_to_map(const hidl_vec<KeyParameter>& params, // Duplicates not allowed for these tags types. return ErrorCode::INVALID_ARGUMENT; } - /* Fall-through! */ + FALLTHROUGH_INTENDED; case TagType::ENUM_REP: + FALLTHROUGH_INTENDED; case TagType::UINT_REP: + FALLTHROUGH_INTENDED; case TagType::ULONG_REP: if (tag_map->find(params[i].tag) == tag_map->end()) { vector<KeyParameter> v{params[i]}; |