blob: 13adcc09c0cfda21b5b54402bf9f2bd19d7c49d0 [file] [log] [blame]
/*
* 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 <android-base/logging.h>
#include <android-base/properties.h>
#include <binder/BinderService.h>
#include <binder/IPCThreadState.h>
#include <include/binder/request_id.h>
#include <utils/Printer.h>
#include <codecvt>
#include <locale>
#include <utility>
/*
* 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<typename ... Args>
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<manager::EventManager> event_manager_;
};
static std::atomic<bool> s_service_started_{false};
static std::atomic<bool> 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<ServiceParams*> 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<std::codecvt_utf8_utf16<char16_t>,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<IIorapImpl::Impl*> 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<IIorapImpl::Impl*> impl_;
};
public:
~Impl() {
package_manager_->UnregisterPackageChangeObserver(package_change_observer_);
}
void SetTaskListener(const ::android::sp<ITaskListener>& listener) {
::android::sp<ITaskListener> 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<ITaskListener> 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<ITaskListener> 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 <name>: Compile single package on device");
printer.printLine(" --purge-package <name>: 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<ITaskListener> 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<manager::TaskResultCallbacks>(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<EventManagerTaskCallbacks> event_manager_callbacks_;
android::sp<PackageChangeObserver> package_change_observer_;
android::sp<PackageManagerDeathRecipient> package_manager_death_recipient_;
std::shared_ptr<PackageManagerRemote> 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<manager::EventManager> 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<IIorapImpl>::publish();
if (ret != android::OK) {
LOG(ERROR) << "BinderService::publish failed with error code: " << ret;
return false;
}
android::sp<android::ProcessState> 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 <typename ... Args>
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 <typename ... Args>
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<Args&&>(rest)...);
return Status::ok();
}
template <typename ... Args>
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 <typename ... Args>
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 <typename ... Args>
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 <typename ... Args>
Status Send(const char* function_name, Args&&... args) {
LOG(VERBOSE) << "IIorap::Send(" << function_name << ")";
return SendArgs(function_name, std::forward<Args>(args)...);
}
} // namespace <anonymous>
} // namespace binder
} // namespace iorap