/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include using aidl::android::frameworks::stats::IStats; using aidl::android::frameworks::stats::VendorAtom; using aidl::android::frameworks::stats::VendorAtomValue; using android::hardware::google::pixel::vendor::PixelAtoms::Atom; using android::hardware::google::pixel::vendor::PixelAtoms::ReverseDomainNames; static void show_usage_and_exit(int code) { LOG(ERROR) << "usage: trusty_metricsd -d "; exit(code); } static void parse_device_name(int argc, char* argv[], char*& device_name) { static const char* _sopts = "h:d:"; static const struct option _lopts[] = {{"help", no_argument, NULL, 'h'}, {"trusty_dev", required_argument, NULL, 'd'}, {0, 0, 0, 0}}; int opt; int oidx = 0; while ((opt = getopt_long(argc, argv, _sopts, _lopts, &oidx)) != -1) { switch (opt) { case 'd': device_name = strdup(optarg); break; default: LOG(ERROR) << "unrecognized option: " << opt; show_usage_and_exit(EXIT_FAILURE); } } if (device_name == nullptr) { LOG(ERROR) << "missing required argument(s)"; show_usage_and_exit(EXIT_FAILURE); } LOG(INFO) << "starting trusty_metricsd"; LOG(INFO) << "trusty dev: " << device_name; } namespace android { namespace trusty { namespace metrics { class TrustyMetricsPixel : public TrustyMetrics { private: TrustyMetricsPixel(std::string tipc_dev, std::shared_ptr stats_client) : TrustyMetrics(std::move(tipc_dev)), stats_client_(stats_client) {} std::shared_ptr stats_client_; public: static std::unique_ptr Create(std::string tipc_dev) { const std::string stats_service_name = std::string(IStats::descriptor).append("/default"); std::shared_ptr stats_client = IStats::fromBinder(ndk::SpAIBinder( AServiceManager_waitForService(stats_service_name.c_str()))); if (!stats_client) { LOG(ERROR) << "failed to open IStats client"; return nullptr; } std::unique_ptr metrics( new TrustyMetricsPixel(std::move(tipc_dev), stats_client)); if (!metrics) { LOG(ERROR) << "failed to allocate TrustyMetricsPixel"; return nullptr; } auto ret = metrics->Open(); if (!ret.ok()) { LOG(ERROR) << "failed to open TrustyMetricsPixel: " << ret.error(); return nullptr; } return metrics; } virtual void HandleCrash(const std::string& app_id) override { LOG(ERROR) << "TA crashed: " << app_id; std::vector v(1); v[0].set(app_id); VendorAtom atom{ .reverseDomainName = ReverseDomainNames().pixel(), .atomId = Atom::kTrustyAppCrashed, .values = std::move(v), }; auto ret = stats_client_->reportVendorAtom(atom); if (!ret.isOk()) { LOG(ERROR) << "failed to report Trusty app crash to IStats"; } } virtual void HandleEventDrop() override { LOG(ERROR) << "Trusty metrics event dropped!"; } }; } // namespace metrics } // namespace trusty } // namespace android int main(int argc, char* argv[]) { char* device_name; parse_device_name(argc, argv, device_name); auto metrics = android::trusty::metrics::TrustyMetricsPixel::Create(device_name); if (!metrics) { LOG(ERROR) << "failed to open Trusty metrics client"; return EXIT_FAILURE; } int binder_fd; binder_status_t status = ABinderProcess_setupPolling(&binder_fd); if (status != STATUS_OK || binder_fd < 0) { LOG(ERROR) << "failed to setup binder polling"; return EXIT_FAILURE; } struct pollfd pfds[] = { { .fd = binder_fd, .events = POLLIN, }, { .fd = metrics->GetRawFd(), .events = POLLIN, }, }; while (1) { int rc = poll(pfds, 2, -1 /* no timeout */); if (rc <= 0) { LOG(ERROR) << "failed poll()"; continue; } short binder_revents = pfds[0].revents; short metrics_revents = pfds[1].revents; short error_mask = POLLHUP | POLLERR | POLLNVAL; if ((binder_revents & error_mask) || (metrics_revents & error_mask)) { LOG(ERROR) << "received an error event from poll()"; /* irrecoverable error */ break; } if (binder_revents & POLLIN) { ABinderProcess_handlePolledCommands(); } if (metrics_revents & POLLIN) { metrics->HandleEvent(); } } return EXIT_FAILURE; }