/*
 * Copyright (c) 2017, The Linux Foundation. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *    * Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *    * Redistributions in binary form must reproduce the above
 *      copyright notice, this list of conditions and the following
 *      disclaimer in the documentation and/or other materials provided
 *      with the distribution.
 *    * Neither the name of The Linux Foundation nor the names of its
 *      contributors may be used to endorse or promote products derived
 *      from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef _HAL_H_
#define _HAL_H_

/* HIDL Includes */
#include <android/hardware/tetheroffload/config/1.0/IOffloadConfig.h>
#include <android/hardware/tetheroffload/control/1.0/IOffloadControl.h>
#include <hidl/HidlTransportSupport.h>

/* External Includes */
#include <string>
#include <vector>

/* Internal Includes */
#include "CtUpdateAmbassador.h"
#include "IOffloadManager.h"
#include "IpaEventRelay.h"
#include "LocalLogBuffer.h"

/* Avoid the namespace litering everywhere */
using ::android::hardware::configureRpcThreadpool;
using ::android::hardware::joinRpcThreadpool;
using ::android::hardware::Return;
using ::android::hardware::hidl_handle;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;

using RET = ::IOffloadManager::RET;
using Prefix = ::IOffloadManager::Prefix;

using ::std::map;
using ::std::string;
using ::std::vector;

using ::android::hardware::tetheroffload::config::V1_0::IOffloadConfig;
using ::android::hardware::tetheroffload::control::V1_0::IOffloadControl;

using ::android::hardware::tetheroffload::control::V1_0::ITetheringOffloadCallback;


class HAL : public IOffloadControl, IOffloadConfig {
public:
    /* Static Const Definitions */
    static const uint32_t UDP_SUBSCRIPTIONS =
            NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY;
    static const uint32_t TCP_SUBSCRIPTIONS =
            NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY;

    /* Interface to IPACM */
    /**
     * @TODO This will likely need to be extended into a proper FactoryPattern
     * when version bumps are needed.
     *
     * This makeIPAHAL function would move to a HALFactory class.  Each HAL could
     * then be versioned (class HAL_V1, class HAL_V2, etc) and inherit from a base class HAL.
     * Then the version number in this function could be used to decide which one to return
     * (if any).
     *
     * IPACM does not need to talk directly back to the returned HAL class.  The other methods that
     * IPACM needs to call are covered by registering the event listeners.  If IPACM did need to
     * talk directly back to the HAL object, without HAL registering a callback, these methods would
     * need to be defined in the HAL base class.
     *
     * This would slightly break backwards compatibility so it should be discouraged; however, the
     * base class could define a sane default implementation and not require that the child class
     * implement this new method.  This "sane default implementation" might only be possible in the
     * case of listening to async events; if IPACM needs to query something, then this would not
     * be backwards compatible and should be done via registering a callback so that IPACM knows
     * this version of HAL supports that functionality.
     *
     * The above statements assume that only one version of the HAL will be instantiated at a time.
     * Yet, it seems possible that a HAL_V1 and HAL_V2 service could both be registered, extending
     * support to both old and new client implementations.  It would be difficult to multiplex
     * information from both versions.  Additionally, IPACM would be responsible for instantiating
     * two HALs (makeIPAHAL(1, ...); makeIPAHAL(2, ...)) which makes signaling between HAL versions
     * (see next paragraph) slightly more difficult but not impossible.
     *
     * If concurrent versions of HAL are required, there will likely need to only be one master.
     * Whichever version of HAL receives a client first may be allowed to take over control while
     * other versions would be required to return failures (ETRYAGAIN: another version in use) until
     * that version of the client relinquishes control.  This should work seemlessly because we
     * currently have an assumption that only one client will be present in system image.
     * Logically, that client will have only a single version (or if it supports multiple, it will
     * always attempt the newest version of HAL before falling back) and therefore no version
     * collisions could possibly occur.
     *
     * Dislaimer:
     * ==========
     * Supporting multiple versions of an interface, in the same code base, at runtime, comes with a
     * significant carrying cost and overhead in the form of developer headaches.  This should not
     * be done lightly and should be extensively scoped before committing to the effort.
     *
     * Perhaps the notion of minor version could be introduced to bridge the gaps created above.
     * For example, 1.x and 1.y could be ran concurrently and supported from the same IPACM code.
     * Yet, a major version update, would not be backwards compatible.  This means that a 2.x HAL
     * could not linked into the same IPACM code base as a 1.x HAL.
     */
    static HAL* makeIPAHAL(int /* version */, IOffloadManager* /* mgr */);

    /* IOffloadConfig */
    Return<void> setHandles(
            const hidl_handle& /* fd1 */,
            const hidl_handle& /* fd2 */,
            setHandles_cb /* hidl_cb */);

    /* IOffloadControl */
    Return<void> initOffload(
            const ::android::sp<ITetheringOffloadCallback>& /* cb */,
            initOffload_cb /* hidl_cb */);
    Return<void> stopOffload(
            stopOffload_cb /* hidl_cb */);
    Return<void> setLocalPrefixes(
            const hidl_vec<hidl_string>& /* prefixes */,
            setLocalPrefixes_cb /* hidl_cb */);
    Return<void> getForwardedStats(
            const hidl_string& /* upstream */,
            getForwardedStats_cb /* hidl_cb */);
    Return<void> setDataLimit(
            const hidl_string& /* upstream */,
            uint64_t /* limit */,
            setDataLimit_cb /* hidl_cb */);
    Return<void> setUpstreamParameters(
            const hidl_string& /* iface */,
            const hidl_string& /* v4Addr */,
            const hidl_string& /* v4Gw */,
            const hidl_vec<hidl_string>& /* v6Gws */,
            setUpstreamParameters_cb /* hidl_cb */);
    Return<void> addDownstream(
            const hidl_string& /* iface */,
            const hidl_string& /* prefix */,
            addDownstream_cb /* hidl_cb */);
    Return<void> removeDownstream(
            const hidl_string& /* iface */,
            const hidl_string& /* prefix */,
            removeDownstream_cb /* hidl_cb */);

    /* Common */
    Return<void> debug(const hidl_handle& /* fd */, const hidl_vec<hidl_string>& /* options */);

private:
    typedef struct BoolResult {
        bool success;
        string errMsg;
    } boolResult_t;

    HAL(IOffloadManager* /* mgr */);
    void registerAsSystemService();
    void doLogcatDump();

    static BoolResult makeInputCheckFailure(string /* customErr */);
    static BoolResult ipaResultToBoolResult(RET /* in */);

    static vector<string> convertHidlStrToStdStr(hidl_vec<hidl_string> /* in */);

    void registerEventListeners();
    void registerIpaCb();
    void registerCtCb();
    void unregisterEventListeners();
    void unregisterIpaCb();
    void unregisterCtCb();

    void clearHandles();

    bool isInitialized();

    IOffloadManager* mIPA;
    hidl_handle mHandle1;
    hidl_handle mHandle2;
    LocalLogBuffer mLogs;
    ::android::sp<ITetheringOffloadCallback> mCb;
    IpaEventRelay *mCbIpa;
    CtUpdateAmbassador *mCbCt;
}; /* HAL */
#endif /* _HAL_H_ */