普通文本  |  272行  |  8.84 KB

/*
 * 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>

/*
 * 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_;

}  // namespace anonymous

class IIorapImpl::Impl {
 public:
  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(WARNING) << "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);
    }
  }

  bool OnAppLaunchEvent(const RequestId& request_id,
                        const AppLaunchEvent& event) {
    if (MaybeHandleFakeBehavior(request_id)) {
      return true;
    }

    return service_params_.event_manager_->OnAppLaunchEvent(request_id, event);
  }

  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)} {
    CHECK(service_params_.event_manager_ != nullptr);
  }

  ServiceParams service_params_;
};

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);

  return true;
}

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 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