/* * Copyright (C) 2018 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 "binder/iiorap_impl.h" #include "binder/iiorap_def.h" #include "common/macros.h" #include "manager/event_manager.h" #include #include #include #include #include #include #include #include #include /* * Definitions for the IIorap binder native service implementation. * See also IIorap.aidl. */ using Status = ::android::binder::Status; using ITaskListener = ::com::google::android::startop::iorap::ITaskListener; namespace iorap { namespace binder { namespace { // Forward declarations. template Status Send(const char* function_name, Args&& ... args); } // Join all parameter declarations by splitting each parameter with a comma. // Types are used fully. #define IIORAP_IMPL_ARG_DECLARATIONS(...) \ IORAP_PP_MAP_SEP(IORAP_BINDER_PARAM_JOIN_ALL, IORAP_PP_COMMA, __VA_ARGS__) #define IIORAP_IMPL_ARG_NAMES(...) \ IORAP_PP_MAP_SEP(IORAP_BINDER_PARAM_JOIN_NAMES, IORAP_PP_COMMA, __VA_ARGS__) #define IIORAP_IMPL_BODY(name, ...) \ ::android::binder::Status IIorapImpl::name(IIORAP_IMPL_ARG_DECLARATIONS(__VA_ARGS__)) { \ return Send(#name, impl_.get(), IIORAP_IMPL_ARG_NAMES(__VA_ARGS__)); \ } IIORAP_IFACE_DEF(/*begin*/IORAP_PP_NOP, IIORAP_IMPL_BODY, /*end*/IORAP_PP_NOP); #undef IIORAP_IMPL_BODY #undef IIORAP_IMPL_ARG_NAMES #undef IIORAP_IMPL_ARGS namespace { struct ServiceParams { bool fake_{false}; std::shared_ptr event_manager_; }; static std::atomic s_service_started_{false}; static std::atomic s_service_params_ready_{false}; // TODO: BinderService constructs IIorapImpl, // but how do I get a pointer to it afterwards? // // This is a workaround for that, by using a global. static ServiceParams s_service_params_; static std::atomic s_service_params_atomic_; // Convert an android::String16 (UTF-16) to a UTF-8 std::string. static std::string String16ToStdString(const ::android::String16& s16) { std::u16string u16{s16.string()}; std::wstring_convert,char16_t> convert; std::string res = convert.to_bytes(u16); return res; } } // namespace anonymous class IIorapImpl::Impl { // ITaskListener implementation for iorap::manager::EventManager. struct EventManagerTaskCallbacks : public iorap::manager::TaskResultCallbacks { explicit EventManagerTaskCallbacks(iorap::borrowed impl) { CHECK(impl != nullptr); impl_ = impl; } virtual void OnProgress(iorap::binder::RequestId request_id, iorap::binder::TaskResult task_result) override { impl_->ReplyWithResult(request_id, /*completed*/false, std::move(task_result)); } virtual void OnComplete(iorap::binder::RequestId request_id, iorap::binder::TaskResult task_result) override { impl_->ReplyWithResult(request_id, /*completed*/true, std::move(task_result)); } virtual ~EventManagerTaskCallbacks() {} iorap::borrowed impl_; }; public: ~Impl() { package_manager_->UnregisterPackageChangeObserver(package_change_observer_); } void SetTaskListener(const ::android::sp& listener) { ::android::sp old_listener = listener_; if (old_listener != nullptr && listener != nullptr) { LOG(WARNING) << "IIorap::setTaskListener: already had a task listener set"; } listener_ = listener; } void ReplyWithResult(const RequestId& request_id, TaskResult::State result_state) { ::android::sp listener = listener_; if (listener == nullptr) { // No listener. Cannot send anything back to the client. // This could be normal, e.g. client had set listener to null before disconnecting. LOG(DEBUG) << "Drop result, no listener registered."; // TODO: print the result with ostream operator<< return; } TaskResult result; result.state = result_state; // TODO: verbose, not info. if (result_state == TaskResult::State::kCompleted) { LOG(VERBOSE) << "ITaskListener::onComplete (request_id=" << request_id.request_id << ")"; listener->onComplete(request_id, result); } else { LOG(VERBOSE) << "ITaskListener::onProgress (request_id=" << request_id.request_id << ")"; listener->onProgress(request_id, result); } } void ReplyWithResult(const RequestId& request_id, bool completed, TaskResult result) { ::android::sp listener = listener_; if (listener == nullptr) { // No listener. Cannot send anything back to the client. // This could be normal, e.g. client had set listener to null before disconnecting. LOG(DEBUG) << "Drop result, no listener registered."; // TODO: print the result with ostream operator<< return; } // TODO: verbose, not info. if (completed) { LOG(VERBOSE) << "ITaskListener::onComplete (request_id=" << request_id.request_id << ")"; listener->onComplete(request_id, result); } else { LOG(VERBOSE) << "ITaskListener::onProgress (request_id=" << request_id.request_id << ")"; listener->onProgress(request_id, result); } } bool OnAppLaunchEvent(const RequestId& request_id, const AppLaunchEvent& event) { if (MaybeHandleFakeBehavior(request_id)) { return true; } return service_params_.event_manager_->OnAppLaunchEvent(request_id, event); } bool OnDexOptEvent(const RequestId& request_id, const DexOptEvent& event) { if (MaybeHandleFakeBehavior(request_id)) { return true; } return service_params_.event_manager_->OnDexOptEvent(request_id, event); } bool OnJobScheduledEvent(const RequestId& request_id, const JobScheduledEvent& event) { if (MaybeHandleFakeBehavior(request_id)) { return true; } return service_params_.event_manager_->OnJobScheduledEvent(request_id, event); } void Dump(/*borrow*/::android::Printer& printer, const ::android::Vector<::android::String16>& args) { if (args.size() == 0) { service_params_.event_manager_->Dump(/*borrow*/printer); return; } ::android::String16 arg_prev; for (const ::android::String16& arg : args) { bool unknown = false; if (arg == ::android::String16("--all") || arg == ::android::String16("-a")) { // using 'dumpsys' or 'bugreport' passes a single '-a' flag to this function. service_params_.event_manager_->Dump(/*borrow*/printer); } else if (arg == ::android::String16("--refresh-properties")) { service_params_.event_manager_->RefreshSystemProperties(/*borrow*/printer); printer.printLine("System properties refreshed."); } else if (arg == ::android::String16("--compile-package")) { // Intentionally left blank. } else if (arg_prev == ::android::String16("--compile-package")) { std::string package_name = String16ToStdString(arg); if (!service_params_.event_manager_->CompilePackage(/*borrow*/printer, package_name)) { printer.printFormatLine("Failed to compile package %s.", package_name.c_str()); } else { printer.printFormatLine("Package %s compiled.", package_name.c_str()); } } else if (arg == ::android::String16("--purge-package")) { // Intentionally left blank. } else if (arg_prev == ::android::String16("--purge-package")) { std::string package_name = String16ToStdString(arg); if (!service_params_.event_manager_->PurgePackage(/*borrow*/printer, package_name)) { printer.printFormatLine("Failed to purge package %s.", package_name.c_str()); } else { printer.printFormatLine("Package %s purged.", package_name.c_str()); } } else { unknown = true; } if (unknown && arg != ::android::String16("--help")) { printer.printLine("Invalid arguments."); printer.printLine(""); printer.printLine("Arguments were:"); for (const ::android::String16& arg16 : args) { printer.printFormatLine(" '%s'", String16ToStdString(arg16).c_str()); } printer.printLine(""); } if (unknown || arg == ::android::String16("--help")) { printer.printLine("Iorapd dumpsys commands:"); printer.printLine(" (none),--all,-a: Print state information for debugging iorapd."); printer.printLine(" --help: Display this help menu"); printer.printLine(" --compile-package : Compile single package on device"); printer.printLine(" --purge-package : Delete database entries/files for package"); printer.printLine(" --refresh-properties: Refresh system properties"); return; } arg_prev = arg; } } void HandleFakeBehavior(const RequestId& request_id) { DCHECK(service_params_.fake_); // Send these dummy callbacks for testing only. ReplyWithResult(request_id, TaskResult::State::kBegan); ReplyWithResult(request_id, TaskResult::State::kOngoing); ReplyWithResult(request_id, TaskResult::State::kCompleted); } // TODO: Subclass IIorap with a separate fake implementation. bool MaybeHandleFakeBehavior(const RequestId& request_id) { if (service_params_.fake_) { HandleFakeBehavior(request_id); return true; } return false; } ::android::sp listener_; Impl(ServiceParams p) : service_params_{std::move(p)}, event_manager_callbacks_{new EventManagerTaskCallbacks{this}} { CHECK(service_params_.event_manager_ != nullptr); service_params_.event_manager_->SetTaskResultCallbacks( std::static_pointer_cast(event_manager_callbacks_)); // Init the package change observer. package_manager_ = PackageManagerRemote::Create(); if (package_manager_ == nullptr) { LOG(ERROR) << "Failed to get package manager service in IIorapImpl::Impl." << " Is system_server down?"; exit(1); } package_change_observer_ = new PackageChangeObserver(service_params_.event_manager_); package_manager_death_recipient_ = new PackageManagerDeathRecipient(package_manager_, package_change_observer_); package_manager_->RegisterPackageChangeObserver(package_change_observer_); package_manager_-> RegisterPackageManagerDeathRecipient(package_manager_death_recipient_); } ServiceParams service_params_; std::shared_ptr event_manager_callbacks_; android::sp package_change_observer_; android::sp package_manager_death_recipient_; std::shared_ptr package_manager_; }; using Impl = IIorapImpl::Impl; IIorapImpl::IIorapImpl() { // Acquire edge of synchronizes-with IIorapImpl::Start(). CHECK(s_service_params_ready_.load()); // Do not turn this into a DCHECK, the above atomic load // must happen-before the read of s_service_params_ready_. impl_.reset(new Impl(std::move(s_service_params_))); } namespace { static bool started_ = false; } bool IIorapImpl::Start(std::shared_ptr event_manager) { if (s_service_started_.load()) { // Acquire-edge (see bottom of function). // Note: Not meant to be idempotent. Two threads could race, and the second // one would likely fail the publish. LOG(ERROR) << "service was already started"; return false; // Already started } CHECK(event_manager != nullptr); { // This block of code needs to happen-before IIorapImpl::IIorapImpl. // TODO: There should be a simpler way of passing down // this data which doesn't involve globals and memory synchronization. ServiceParams* p = &s_service_params_; // TODO: move all property reads to a dedicated Config class. p->fake_ = ::android::base::GetBoolProperty("iorapd.binder.fake", /*default*/false); p->event_manager_ = std::move(event_manager); // Release edge of synchronizes-with IIorapImpl::IIorapImpl. s_service_params_ready_.store(true); } ::android::IPCThreadState::self()->disableBackgroundScheduling(/*disable*/true); ::android::status_t ret = android::BinderService::publish(); if (ret != android::OK) { LOG(ERROR) << "BinderService::publish failed with error code: " << ret; return false; } android::sp ps = android::ProcessState::self(); // Reduce thread consumption by only using 1 thread. // We should also be able to leverage this by avoiding locks, etc. ps->setThreadPoolMaxThreadCount(/*maxThreads*/1); ps->startThreadPool(); ps->giveThreadPoolName(); // Release edge synchronizes-with the top of this function. s_service_started_.store(true); // TODO: IIRC thread-start(t1) synchronizes-with t1.main, so we should be able // to delete the majority of atomics for any pre-thread-start initialization... return true; } ::android::status_t IIorapImpl::dump(int fd, const ::android::Vector<::android::String16>& args) { ::android::FdPrinter printer{fd}; impl_->Dump(printer, args); return ::android::NO_ERROR; } namespace { #define MAYBE_HAVE_FAKE_BEHAVIOR(self, request_id) \ if (self->MaybeHandleFakeBehavior(request_id)) { return ::android::binder::Status::ok(); } template Status SendArgs(const char* function_name, Impl* self, const RequestId& request_id, Args&&... /*rest*/) { LOG(VERBOSE) << "IIorap::" << function_name << " (request_id = " << request_id.request_id << ")"; MAYBE_HAVE_FAKE_BEHAVIOR(self, request_id); // TODO: implementation. LOG(ERROR) << "IIorap::" << function_name << " -- not implemented for real code"; return Status::fromStatusT(::android::INVALID_OPERATION); } template Status SendArgs(const char* function_name, Impl* self, Args&&... rest) { DCHECK_EQ(std::string(function_name), "setTaskListener"); LOG(VERBOSE) << "IIorap::setTaskListener"; self->SetTaskListener(std::forward(rest)...); return Status::ok(); } template Status SendArgs(const char* function_name, Impl* self, const RequestId& request_id, const AppLaunchEvent& app_launch_event) { DCHECK_EQ(std::string(function_name), "onAppLaunchEvent"); LOG(VERBOSE) << "IIorap::onAppLaunchEvent"; MAYBE_HAVE_FAKE_BEHAVIOR(self, request_id); if (self->OnAppLaunchEvent(request_id, app_launch_event)) { return Status::ok(); } else { // TODO: I suppose this should write out an exception back, // like a service-specific error or something. // // It depends on whether or not we even have any synchronous // errors. // // Most of the work here is done async, so it should handle // async callbacks. return Status::fromStatusT(::android::BAD_VALUE); } } template Status SendArgs(const char* function_name, Impl* self, const RequestId& request_id, const DexOptEvent& event) { DCHECK_EQ(std::string(function_name), "onDexOptEvent"); LOG(VERBOSE) << "IIorap::onDexOptEvent"; MAYBE_HAVE_FAKE_BEHAVIOR(self, request_id); if (self->OnDexOptEvent(request_id, event)) { return Status::ok(); } else { return Status::fromStatusT(::android::BAD_VALUE); } } template Status SendArgs(const char* function_name, Impl* self, const RequestId& request_id, const JobScheduledEvent& event) { DCHECK_EQ(std::string(function_name), "onJobScheduledEvent"); LOG(VERBOSE) << "IIorap::onJobScheduledEvent"; MAYBE_HAVE_FAKE_BEHAVIOR(self, request_id); if (self->OnJobScheduledEvent(request_id, event)) { return Status::ok(); } else { // TODO: I suppose this should write out an exception back, // like a service-specific error or something. // // It depends on whether or not we even have any synchronous // errors. // // Most of the work here is done async, so it should handle // async callbacks. return Status::fromStatusT(::android::BAD_VALUE); } } template Status Send(const char* function_name, Args&&... args) { LOG(VERBOSE) << "IIorap::Send(" << function_name << ")"; return SendArgs(function_name, std::forward(args)...); } } // namespace } // namespace binder } // namespace iorap