/*
 * Copyright (C) 2017 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.
 */

#ifndef ART_ADBCONNECTION_ADBCONNECTION_H_
#define ART_ADBCONNECTION_ADBCONNECTION_H_

#include <jni.h>
#include <stdint.h>
#include <sys/socket.h>
#include <sys/un.h>

#include <limits>
#include <memory>
#include <string>
#include <vector>

#include "adbconnection/client.h"
#include "base/array_ref.h"
#include "base/mutex.h"
#include "runtime_callbacks.h"

namespace adbconnection {

static constexpr char kAdbConnectionThreadName[] = "ADB-JDWP Connection Control Thread";

// The default jdwp agent name.
static constexpr char kDefaultJdwpAgentName[] = "libjdwp.so";

class AdbConnectionState;

struct AdbConnectionDebuggerController : public art::DebuggerControlCallback {
  explicit AdbConnectionDebuggerController(AdbConnectionState* connection)
      : connection_(connection) {}

  // Begin running the debugger.
  void StartDebugger() override;

  // The debugger should begin shutting down since the runtime is ending.
  void StopDebugger() override;

  bool IsDebuggerConfigured() override;

 private:
  AdbConnectionState* connection_;
};

enum class DdmPacketType : uint8_t { kReply = 0x80, kCmd = 0x00, };

struct AdbConnectionDdmCallback : public art::DdmCallback {
  explicit AdbConnectionDdmCallback(AdbConnectionState* connection) : connection_(connection) {}

  void DdmPublishChunk(uint32_t type,
                       const art::ArrayRef<const uint8_t>& data)
      REQUIRES_SHARED(art::Locks::mutator_lock_);

 private:
  AdbConnectionState* connection_;
};

struct AdbConnectionAppInfoCallback : public art::AppInfoCallback {
  explicit AdbConnectionAppInfoCallback(AdbConnectionState* connection) : connection_(connection) {}

  void AddApplication(const std::string& package_name) REQUIRES_SHARED(art::Locks::mutator_lock_);
  void RemoveApplication(const std::string& package_name)
      REQUIRES_SHARED(art::Locks::mutator_lock_);
  void SetCurrentProcessName(const std::string& process_name)
      REQUIRES_SHARED(art::Locks::mutator_lock_);
  void SetWaitingForDebugger(bool waiting) REQUIRES_SHARED(art::Locks::mutator_lock_);
  void SetUserId(int uid) REQUIRES_SHARED(art::Locks::mutator_lock_);

 private:
  AdbConnectionState* connection_;
};

class AdbConnectionState {
 public:
  explicit AdbConnectionState(const std::string& name);
  ~AdbConnectionState();

  // Called on the listening thread to start dealing with new input. thr is used to attach the new
  // thread to the runtime.
  void RunPollLoop(art::Thread* self);

  // Sends ddms data over the socket, if there is one. This data is sent even if we haven't finished
  // hand-shaking yet.
  void PublishDdmData(uint32_t type, const art::ArrayRef<const uint8_t>& data);

  // Wake up the poll. Call this if the set of interesting event has changed. They will be
  // recalculated and poll will be called again via fdevent write. This wakeup  relies on fdevent
  // and should be ACKed via AcknowledgeWakeup.
  void WakeupPollLoop();

  // After a call to WakeupPollLoop, the fdevent internal counter should be decreased via
  // this method. This should be called after WakeupPollLoop was called and poll triggered.
  void AcknowledgeWakeup();

  // Stops debugger threads during shutdown.
  void StopDebuggerThreads();

  // If StartDebuggerThreads was called successfully.
  bool DebuggerThreadsStarted() {
    return started_debugger_threads_;
  }

  // Should be called by Framework when it changes its process name.
  void SetCurrentProcessName(const std::string& process_name);

  // Should be called by Framework when it adds an app to a process.
  // This can be called several times (see android:process)
  void AddApplication(const std::string& package_name);

  // Should be called by Framework when it removes an app from a process.
  void RemoveApplication(const std::string& package_name);

  // Should be called by Framework when its debugging state changes.
  void SetWaitingForDebugger(bool waiting);

  // Should be called by Framework when the UserID is known.
  void SetUserId(int uid);

 private:
  uint32_t NextDdmId();

  void StartDebuggerThreads();

  // Tell adbd about the new runtime.
  bool SetupAdbConnection();

  std::string MakeAgentArg();

  void SendAgentFds(bool require_handshake);

  void CloseFds();

  void HandleDataWithoutAgent(art::Thread* self);

  void PerformHandshake();

  void AttachJdwpAgent(art::Thread* self);

  void NotifyDdms(bool active);

  void SendDdmPacket(uint32_t id,
                     DdmPacketType type,
                     uint32_t ddm_type,
                     art::ArrayRef<const uint8_t> data);

  std::string agent_name_;

  AdbConnectionDebuggerController controller_;
  AdbConnectionDdmCallback ddm_callback_;
  AdbConnectionAppInfoCallback appinfo_callback_;

  // Eventfd used to allow the StopDebuggerThreads function to wake up sleeping threads
  android::base::unique_fd sleep_event_fd_;

  // Context which wraps the socket which we use to talk to adbd.
  std::unique_ptr<AdbConnectionClientContext, void(*)(AdbConnectionClientContext*)> control_ctx_;

  // Socket that we use to talk to the agent (if it's loaded).
  android::base::unique_fd local_agent_control_sock_;

  // The fd of the socket the agent uses to talk to us. We need to keep it around in order to clean
  // it up when the runtime goes away.
  android::base::unique_fd remote_agent_control_sock_;

  // The fd that is forwarded through adb to the client. This is guarded by the
  // adb_write_event_fd_.
  android::base::unique_fd adb_connection_socket_;

  // The fd we send to the agent to let us synchronize access to the shared adb_connection_socket_.
  // This is also used as a general lock for the adb_connection_socket_ on any threads other than
  // the poll thread.
  android::base::unique_fd adb_write_event_fd_;

  std::atomic<bool> shutting_down_;

  // True if we have loaded the agent library.
  std::atomic<bool> agent_loaded_;

  // True if the dt_fd_forward transport is listening for a new communication channel.
  std::atomic<bool> agent_listening_;

  // True if the dt_fd_forward transport has the socket. If so we don't do anything to the agent or
  // the adb connection socket until connection goes away.
  std::atomic<bool> agent_has_socket_;

  std::atomic<bool> sent_agent_fds_;

  std::atomic<bool> performed_handshake_;

  bool notified_ddm_active_;

  std::atomic<uint32_t> next_ddm_id_;

  bool started_debugger_threads_;

  friend struct AdbConnectionDebuggerController;
};

}  // namespace adbconnection

#endif  // ART_ADBCONNECTION_ADBCONNECTION_H_
