/*
 * Copyright (C) 2015 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 ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
#define ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H

#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <utils/Timers.h>
#include <utils/Log.h>

#include <algorithm>
#include <utility>
#include <vector>
#include <set>
#include <map>
#include <memory>
#include <com_android_internal_camera_flags.h>

namespace flags = com::android::internal::camera::flags;

namespace android {
namespace resource_policy {

// Values from frameworks/base/services/core/java/com/android/server/am/ProcessList.java
const int32_t INVALID_ADJ = -10000;
const int32_t UNKNOWN_ADJ = 1001;
const int32_t CACHED_APP_MAX_ADJ = 999;
const int32_t CACHED_APP_MIN_ADJ = 900;
const int32_t CACHED_APP_LMK_FIRST_ADJ = 950;
const int32_t CACHED_APP_IMPORTANCE_LEVELS = 5;
const int32_t SERVICE_B_ADJ = 800;
const int32_t PREVIOUS_APP_ADJ = 700;
const int32_t HOME_APP_ADJ = 600;
const int32_t SERVICE_ADJ = 500;
const int32_t HEAVY_WEIGHT_APP_ADJ = 400;
const int32_t BACKUP_APP_ADJ = 300;
const int32_t PERCEPTIBLE_LOW_APP_ADJ = 250;
const int32_t PERCEPTIBLE_MEDIUM_APP_ADJ = 225;
const int32_t PERCEPTIBLE_APP_ADJ = 200;
const int32_t VISIBLE_APP_ADJ = 100;
const int32_t VISIBLE_APP_LAYER_MAX = PERCEPTIBLE_APP_ADJ - VISIBLE_APP_ADJ - 1;
const int32_t PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ = 50;
const int32_t FOREGROUND_APP_ADJ = 0;
const int32_t PERSISTENT_SERVICE_ADJ = -700;
const int32_t PERSISTENT_PROC_ADJ = -800;
const int32_t SYSTEM_ADJ = -900;
const int32_t NATIVE_ADJ = -1000;

class ClientPriority {
public:
    /**
     * Choosing to set mIsVendorClient through a parameter instead of calling
     * getCurrentServingCall() == BinderCallType::HWBINDER to protect against the
     * case where the construction is offloaded to another thread which isn't a
     * hwbinder thread.
     */
    ClientPriority(int32_t score, int32_t state, bool isVendorClient, int32_t scoreOffset = 0) :
            mIsVendorClient(isVendorClient), mScoreOffset(scoreOffset) {
        setScore(score);
        setState(state);
    }

    int32_t getScore() const { return mScore; }
    int32_t getState() const { return mState; }
    int32_t isVendorClient() const { return mIsVendorClient; }

    void setScore(int32_t score) {
        // For vendor clients, the score is set once and for all during
        // construction. Otherwise, it can get reset each time cameraserver
        // queries ActivityManagerService for oom_adj scores / states .
        // For clients where the score offset is set by the app, add it to the
        // score provided by ActivityManagerService.
        if (score == INVALID_ADJ) {
            mScore = UNKNOWN_ADJ;
        } else {
            mScore = mScoreOffset + score;
        }
    }

    void setState(int32_t state) {
      // For vendor clients, the score is set once and for all during
      // construction. Otherwise, it can get reset each time cameraserver
      // queries ActivityManagerService for oom_adj scores / states
      // (ActivityManagerService returns a vendor process' state as
      // PROCESS_STATE_NONEXISTENT.
      mState = state;
    }

    bool operator==(const ClientPriority& rhs) const {
        return (this->mScore == rhs.mScore) && (this->mState == rhs.mState);
    }

    bool operator< (const ClientPriority& rhs)  const {
        if (this->mScore == rhs.mScore) {
            return this->mState < rhs.mState;
        } else {
            return this->mScore < rhs.mScore;
        }
    }

    bool operator> (const ClientPriority& rhs) const {
        return rhs < *this;
    }

    bool operator<=(const ClientPriority& rhs) const {
        return !(*this > rhs);
    }

    bool operator>=(const ClientPriority& rhs) const {
        return !(*this < rhs);
    }

private:
        int32_t mScore;
        int32_t mState;
        bool mIsVendorClient = false;
        int32_t mScoreOffset = 0;
};

// --------------------------------------------------------------------------------

/**
 * The ClientDescriptor class is a container for a given key/value pair identifying a shared
 * resource, and the corresponding cost, priority, owner ID, and conflicting keys list used
 * in determining eviction behavior.
 *
 * Aside from the priority, these values are immutable once the ClientDescriptor has been
 * constructed.
 */
template<class KEY, class VALUE>
class ClientDescriptor final {
public:
    ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost,
            const std::set<KEY>& conflictingKeys, int32_t score, int32_t ownerId, int32_t state,
            bool isVendorClient, int32_t oomScoreOffset, bool sharedMode = false);
    ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost, std::set<KEY>&& conflictingKeys,
            int32_t score, int32_t ownerId, int32_t state, bool isVendorClient,
            int32_t oomScoreOffset, bool sharedMode = false);

    ~ClientDescriptor();

    /**
     * Return the key for this descriptor.
     */
    const KEY& getKey() const;

    /**
     * Return the value for this descriptor.
     */
    const VALUE& getValue() const;

    /**
     * Return the cost for this descriptor.
     */
    int32_t getCost() const;

    /**
     * Return the priority for this descriptor.
     */
    const ClientPriority &getPriority() const;

    /**
     * Return the owner ID for this descriptor.
     */
    int32_t getOwnerId() const;

    /**
     * Return true if the given key is in this descriptor's conflicting keys list.
     */
    bool isConflicting(const KEY& key) const;

    /**
     * Return the set of all conflicting keys for this descriptor.
     */
    std::set<KEY> getConflicting() const;

    /**
     * Set the proirity for this descriptor.
     */
    void setPriority(const ClientPriority& priority);

    /**
     * Returns true when camera is opened in shared mode.
     */
    bool getSharedMode() const;

    // This class is ordered by key
    template<class K, class V>
    friend bool operator < (const ClientDescriptor<K, V>& a, const ClientDescriptor<K, V>& b);

private:
    KEY mKey;
    VALUE mValue;
    int32_t mCost;
    std::set<KEY> mConflicting;
    ClientPriority mPriority;
    int32_t mOwnerId;
    bool mSharedMode;
}; // class ClientDescriptor

template<class K, class V>
bool operator < (const ClientDescriptor<K, V>& a, const ClientDescriptor<K, V>& b) {
    return a.mKey < b.mKey;
}

template<class KEY, class VALUE>
ClientDescriptor<KEY, VALUE>::ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost,
        const std::set<KEY>& conflictingKeys, int32_t score, int32_t ownerId, int32_t state,
        bool isVendorClient, int32_t scoreOffset, bool sharedMode) :
        mKey{key}, mValue{value}, mCost{cost}, mConflicting{conflictingKeys},
        mPriority(score, state, isVendorClient, scoreOffset),
        mOwnerId{ownerId}, mSharedMode{sharedMode} {}

template<class KEY, class VALUE>
ClientDescriptor<KEY, VALUE>::ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost,
        std::set<KEY>&& conflictingKeys, int32_t score, int32_t ownerId, int32_t state,
        bool isVendorClient, int32_t scoreOffset, bool sharedMode) :
        mKey{std::forward<KEY>(key)}, mValue{std::forward<VALUE>(value)}, mCost{cost},
        mConflicting{std::forward<std::set<KEY>>(conflictingKeys)},
        mPriority(score, state, isVendorClient, scoreOffset), mOwnerId{ownerId},
        mSharedMode{sharedMode} {}

template<class KEY, class VALUE>
ClientDescriptor<KEY, VALUE>::~ClientDescriptor() {}

template<class KEY, class VALUE>
const KEY& ClientDescriptor<KEY, VALUE>::getKey() const {
    return mKey;
}

template<class KEY, class VALUE>
const VALUE& ClientDescriptor<KEY, VALUE>::getValue() const {
    return mValue;
}

template<class KEY, class VALUE>
int32_t ClientDescriptor<KEY, VALUE>::getCost() const {
    return mCost;
}

template<class KEY, class VALUE>
const ClientPriority& ClientDescriptor<KEY, VALUE>::getPriority() const {
    return mPriority;
}

template<class KEY, class VALUE>
int32_t ClientDescriptor<KEY, VALUE>::getOwnerId() const {
    return mOwnerId;
}

template<class KEY, class VALUE>
bool ClientDescriptor<KEY, VALUE>::isConflicting(const KEY& key) const {
    if (flags::camera_multi_client()) {
        // In shared mode, there can be more than one client using the camera.
        // Hence, having more than one client with the same key is not considered as
        // conflicting.
        if (!mSharedMode && key == mKey) return true;
    } else {
        if (key == mKey) return true;
    }
    for (const auto& x : mConflicting) {
        if (key == x) return true;
    }
    return false;
}

template<class KEY, class VALUE>
std::set<KEY> ClientDescriptor<KEY, VALUE>::getConflicting() const {
    return mConflicting;
}

template<class KEY, class VALUE>
bool ClientDescriptor<KEY, VALUE>::getSharedMode() const {
    return mSharedMode;
}

template<class KEY, class VALUE>
void ClientDescriptor<KEY, VALUE>::setPriority(const ClientPriority& priority) {
    // We don't use the usual copy constructor here since we want to remember
    // whether a client is a vendor client or not. This could have been wiped
    // off in the incoming priority argument since an AIDL thread might have
    // called getCurrentServingCall() == BinderCallType::HWBINDER after refreshing
    // priorities for old clients through ProcessInfoService::getProcessStatesScoresFromPids().
    if (mPriority.isVendorClient()) {
        return;
    }
    mPriority.setScore(priority.getScore());
    mPriority.setState(priority.getState());
}

// --------------------------------------------------------------------------------

/**
 * A default class implementing the LISTENER interface used by ClientManager.
 */
template<class KEY, class VALUE>
class DefaultEventListener {
public:
    void onClientAdded(const ClientDescriptor<KEY, VALUE>& descriptor);
    void onClientRemoved(const ClientDescriptor<KEY, VALUE>& descriptor);
};

template<class KEY, class VALUE>
void DefaultEventListener<KEY, VALUE>::onClientAdded(
        const ClientDescriptor<KEY, VALUE>& /*descriptor*/) {}

template<class KEY, class VALUE>
void DefaultEventListener<KEY, VALUE>::onClientRemoved(
        const ClientDescriptor<KEY, VALUE>& /*descriptor*/) {}

// --------------------------------------------------------------------------------

/**
 * The ClientManager class wraps an LRU-ordered list of active clients and implements eviction
 * behavior for handling shared resource access.
 *
 * When adding a new descriptor, eviction behavior is as follows:
 *   - Keys are unique, adding a descriptor with the same key as an existing descriptor will
 *     result in the lower-priority of the two being removed.  Priority ties result in the
 *     LRU descriptor being evicted (this means the incoming descriptor be added in this case).
 *   - Any descriptors with keys that are in the incoming descriptor's 'conflicting keys' list
 *     will be removed if they have an equal or lower priority than the incoming descriptor;
 *     if any have a higher priority, the incoming descriptor is removed instead.
 *   - If the sum of all descriptors' costs, including the incoming descriptor's, is more than
 *     the max cost allowed for this ClientManager, descriptors with non-zero cost, equal or lower
 *     priority, and a different owner will be evicted in LRU order until either the cost is less
 *     than the max cost, or all descriptors meeting this criteria have been evicted and the
 *     incoming descriptor has the highest priority.  Otherwise, the incoming descriptor is
 *     removed instead.
 */
template<class KEY, class VALUE, class LISTENER=DefaultEventListener<KEY, VALUE>>
class ClientManager {
public:
    // The default maximum "cost" allowed before evicting
    static constexpr int32_t DEFAULT_MAX_COST = 100;

    ClientManager();
    explicit ClientManager(int32_t totalCost);

    /**
     * Add a given ClientDescriptor to the managed list.  ClientDescriptors for clients that
     * are evicted by this action are returned in a vector.
     *
     * This may return the ClientDescriptor passed in if it would be evicted.
     */
    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> addAndEvict(
            const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client);

    /**
     * Given a map containing owner (pid) -> priority mappings, update the priority of each
     * ClientDescriptor with an owner in this mapping.
     */
    void updatePriorities(const std::map<int32_t,ClientPriority>& ownerPriorityList);

    /**
     * Remove all ClientDescriptors.
     */
    void removeAll();

    /**
     * Remove all ClientDescriptors with a given key.
     */
    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> removeAll(const KEY& key);

    /**
     * Remove and return the ClientDescriptors with a given key.
     */
    std::shared_ptr<ClientDescriptor<KEY, VALUE>> remove(const KEY& key);

    /**
     * Remove the given ClientDescriptor.
     */
    virtual void remove(const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value);

    /**
     * Return a vector of the ClientDescriptors that would be evicted by adding the given
     * ClientDescriptor.
     *
     * This may return the ClientDescriptor passed in if it would be evicted.
     */
    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvict(
            const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const;

    /**
     * Return a vector of active ClientDescriptors that prevent this client from being added.
     */
    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getIncompatibleClients(
            const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const;

    /**
     * Return a vector containing all currently active ClientDescriptors.
     */
    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getAll() const;

    /**
     * Return a vector containing all keys of currently active ClientDescriptors.
     */
    std::vector<KEY> getAllKeys() const;

    /**
     * Return a vector of the owner tags of all currently active ClientDescriptors (duplicates
     * will be removed).
     */
    std::vector<int32_t> getAllOwners() const;

    /**
     * Return the ClientDescriptor corresponding to the given key, or an empty shared pointer
     * if none exists.
     */
    std::shared_ptr<ClientDescriptor<KEY, VALUE>> get(const KEY& key) const;

    std::shared_ptr<ClientDescriptor<KEY, VALUE>> getPrimaryClient(const KEY& key) const;

    /**
     * Block until the given client is no longer in the active clients list, or the timeout
     * occurred.
     *
     * Returns NO_ERROR if this succeeded, -ETIMEDOUT on a timeout, or a negative error code on
     * failure.
     */
    status_t waitUntilRemoved(const std::shared_ptr<ClientDescriptor<KEY, VALUE>> client,
            nsecs_t timeout) const;

    /**
     * Set the current listener for client add/remove events.
     *
     * The listener instance must inherit from the LISTENER class and implement the following
     * methods:
     *    void onClientRemoved(const ClientDescriptor<KEY, VALUE>& descriptor);
     *    void onClientAdded(const ClientDescriptor<KEY, VALUE>& descriptor);
     *
     * These callback methods will be called with the ClientManager's lock held, and should
     * not call any further ClientManager methods.
     *
     * The onClientRemoved method will be called when the client has been removed or evicted
     * from the ClientManager that this event listener has been added to. The onClientAdded
     * method will be called when the client has been added to the ClientManager that this
     * event listener has been added to.
     */
    void setListener(const std::shared_ptr<LISTENER>& listener);

protected:
    ~ClientManager();

private:

    /**
     * Return a vector of the ClientDescriptors that would be evicted by adding the given
     * ClientDescriptor.  If returnIncompatibleClients is set to true, instead, return the
     * vector of ClientDescriptors that are higher priority than the incoming client and
     * either conflict with this client, or contribute to the resource cost if that would
     * prevent the incoming client from being added.
     *
     * This may return the ClientDescriptor passed in.
     */
    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvictLocked(
            const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client,
            bool returnIncompatibleClients = false) const;

    int64_t getCurrentCostLocked() const;

    mutable Mutex mLock;
    mutable Condition mRemovedCondition;
    int32_t mMaxCost;
    // LRU ordered, most recent at end
    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> mClients;
    std::shared_ptr<LISTENER> mListener;
}; // class ClientManager

template<class KEY, class VALUE, class LISTENER>
ClientManager<KEY, VALUE, LISTENER>::ClientManager() :
        ClientManager(DEFAULT_MAX_COST) {}

template<class KEY, class VALUE, class LISTENER>
ClientManager<KEY, VALUE, LISTENER>::ClientManager(int32_t totalCost) : mMaxCost(totalCost) {}

template<class KEY, class VALUE, class LISTENER>
ClientManager<KEY, VALUE, LISTENER>::~ClientManager() {}

template<class KEY, class VALUE, class LISTENER>
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
ClientManager<KEY, VALUE, LISTENER>::wouldEvict(
        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const {
    Mutex::Autolock lock(mLock);
    return wouldEvictLocked(client);
}

template<class KEY, class VALUE, class LISTENER>
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
ClientManager<KEY, VALUE, LISTENER>::getIncompatibleClients(
        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const {
    Mutex::Autolock lock(mLock);
    return wouldEvictLocked(client, /*returnIncompatibleClients*/true);
}

template<class KEY, class VALUE, class LISTENER>
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
ClientManager<KEY, VALUE, LISTENER>::wouldEvictLocked(
        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client,
        bool returnIncompatibleClients) const {

    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> evictList;

    // Disallow null clients, return input
    if (client == nullptr) {
        evictList.push_back(client);
        return evictList;
    }

    const KEY& key = client->getKey();
    int32_t cost = client->getCost();
    ClientPriority priority = client->getPriority();
    int32_t owner = client->getOwnerId();
    bool sharedMode = client->getSharedMode();


    int64_t totalCost = getCurrentCostLocked() + cost;

    // Determine the MRU of the owners tied for having the highest priority
    int32_t highestPriorityOwner = owner;
    ClientPriority highestPriority = priority;
    for (const auto& i : mClients) {
        ClientPriority curPriority = i->getPriority();
        if (curPriority <= highestPriority) {
            highestPriority = curPriority;
            highestPriorityOwner = i->getOwnerId();
        }
    }

    if (highestPriority == priority) {
        // Switch back owner if the incoming client has the highest priority, as it is MRU
        highestPriorityOwner = owner;
    }

    // Build eviction list of clients to remove
    for (const auto& i : mClients) {
        const KEY& curKey = i->getKey();
        int32_t curCost = i->getCost();
        ClientPriority curPriority = i->getPriority();
        int32_t curOwner = i->getOwnerId();
        bool curSharedMode = i->getSharedMode();
        bool conflicting;
        if (flags::camera_multi_client()) {
            conflicting = (((!sharedMode || !curSharedMode) && curKey == key)
                    || i->isConflicting(key) || client->isConflicting(curKey));
        } else {
            conflicting = (curKey == key || i->isConflicting(key) ||
                    client->isConflicting(curKey));
        }

        if (!returnIncompatibleClients) {
            // Find evicted clients

            if (conflicting && owner == curOwner) {
                // Pre-existing conflicting client with the same client owner exists
                // Open the same device twice -> most recent open wins
                // Otherwise let the existing client wins to avoid behaviors difference
                // due to how HAL advertising conflicting devices (which is hidden from
                // application)
                if (curKey == key) {
                    evictList.push_back(i);
                    totalCost -= curCost;
                } else {
                    evictList.clear();
                    evictList.push_back(client);
                    return evictList;
                }
            } else if (conflicting && curPriority < priority) {
                // Pre-existing conflicting client with higher priority exists
                evictList.clear();
                evictList.push_back(client);
                return evictList;
            } else if (conflicting || ((totalCost > mMaxCost && curCost > 0) &&
                    (curPriority >= priority) &&
                    !(highestPriorityOwner == owner && owner == curOwner))) {
                // Add a pre-existing client to the eviction list if:
                // - We are adding a client with higher priority that conflicts with this one.
                // - The total cost including the incoming client's is more than the allowable
                //   maximum, and the client has a non-zero cost, lower priority, and a different
                //   owner than the incoming client when the incoming client has the
                //   highest priority.
                evictList.push_back(i);
                totalCost -= curCost;
            }
        } else {
            // Find clients preventing the incoming client from being added

            if (curPriority < priority && (conflicting || (totalCost > mMaxCost && curCost > 0))) {
                // Pre-existing conflicting client with higher priority exists
                evictList.push_back(i);
            }
        }
    }

    // Immediately return the incompatible clients if we are calculating these instead
    if (returnIncompatibleClients) {
        return evictList;
    }

    // If the total cost is too high, return the input unless the input has the highest priority
    if (totalCost > mMaxCost && highestPriorityOwner != owner) {
        evictList.clear();
        evictList.push_back(client);
        return evictList;
    }

    return evictList;

}

template<class KEY, class VALUE, class LISTENER>
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
ClientManager<KEY, VALUE, LISTENER>::addAndEvict(
        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) {
    Mutex::Autolock lock(mLock);
    auto evicted = wouldEvictLocked(client);
    auto it = evicted.begin();
    if (it != evicted.end() && *it == client) {
        return evicted;
    }

    auto iter = evicted.cbegin();

    if (iter != evicted.cend()) {

        if (mListener != nullptr) mListener->onClientRemoved(**iter);

        // Remove evicted clients from list
        mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
            [&iter] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
                if (curClientPtr->getKey() == (*iter)->getKey()) {
                    iter++;
                    return true;
                }
                return false;
            }), mClients.end());
    }

    if (mListener != nullptr) mListener->onClientAdded(*client);
    mClients.push_back(client);
    mRemovedCondition.broadcast();

    return evicted;
}

template<class KEY, class VALUE, class LISTENER>
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
ClientManager<KEY, VALUE, LISTENER>::getAll() const {
    Mutex::Autolock lock(mLock);
    return mClients;
}

template<class KEY, class VALUE, class LISTENER>
std::vector<KEY> ClientManager<KEY, VALUE, LISTENER>::getAllKeys() const {
    Mutex::Autolock lock(mLock);
    std::vector<KEY> keys(mClients.size());
    for (const auto& i : mClients) {
        keys.push_back(i->getKey());
    }
    return keys;
}

template<class KEY, class VALUE, class LISTENER>
std::vector<int32_t> ClientManager<KEY, VALUE, LISTENER>::getAllOwners() const {
    Mutex::Autolock lock(mLock);
    std::set<int32_t> owners;
    for (const auto& i : mClients) {
        owners.emplace(i->getOwnerId());
    }
    return std::vector<int32_t>(owners.begin(), owners.end());
}

template<class KEY, class VALUE, class LISTENER>
void ClientManager<KEY, VALUE, LISTENER>::updatePriorities(
        const std::map<int32_t,ClientPriority>& ownerPriorityList) {
    Mutex::Autolock lock(mLock);
    for (auto& i : mClients) {
        auto j = ownerPriorityList.find(i->getOwnerId());
        if (j != ownerPriorityList.end()) {
            i->setPriority(j->second);
        }
    }
}

template<class KEY, class VALUE, class LISTENER>
std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE, LISTENER>::get(
        const KEY& key) const {
    Mutex::Autolock lock(mLock);
    for (const auto& i : mClients) {
        if (i->getKey() == key) return i;
    }
    return std::shared_ptr<ClientDescriptor<KEY, VALUE>>(nullptr);
}

template<class KEY, class VALUE, class LISTENER>
std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE, LISTENER>::getPrimaryClient(
        const KEY& key) const {
    Mutex::Autolock lock(mLock);
    if (flags::camera_multi_client()) {
        for (const auto& i : mClients) {
            bool sharedMode =  i->getSharedMode();
            bool primaryClient;
            status_t ret = i->getValue()->isPrimaryClient(&primaryClient);
            if (ret == OK) {
                if ((i->getKey() == key) && sharedMode && primaryClient) {
                    return i;
                }
            }
        }
    }
    return std::shared_ptr<ClientDescriptor<KEY, VALUE>>(nullptr);
}

template<class KEY, class VALUE, class LISTENER>
void ClientManager<KEY, VALUE, LISTENER>::removeAll() {
    Mutex::Autolock lock(mLock);
    if (mListener != nullptr) {
        for (const auto& i : mClients) {
            mListener->onClientRemoved(*i);
        }
    }
    mClients.clear();
    mRemovedCondition.broadcast();
}

template<class KEY, class VALUE, class LISTENER>
std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
        ClientManager<KEY, VALUE, LISTENER>::removeAll(const KEY& key) {
    Mutex::Autolock lock(mLock);
    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> clients;
    if (flags::camera_multi_client()) {
        for (auto it = mClients.begin(); it != mClients.end();)
        {
            if ((*it)->getKey() == key) {
                if (mListener != nullptr) mListener->onClientRemoved(**it);
                clients.push_back(*it);
                it = mClients.erase(it);
            } else {
                ++it;
            }
        }
        mRemovedCondition.broadcast();
    }
    return clients;
}

template<class KEY, class VALUE, class LISTENER>
std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE, LISTENER>::remove(
    const KEY& key) {
    Mutex::Autolock lock(mLock);

    std::shared_ptr<ClientDescriptor<KEY, VALUE>> ret;

    // Remove evicted clients from list
    mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
        [this, &key, &ret] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
            if (curClientPtr->getKey() == key) {
                if (mListener != nullptr) mListener->onClientRemoved(*curClientPtr);
                ret = curClientPtr;
                return true;
            }
            return false;
        }), mClients.end());

    mRemovedCondition.broadcast();
    return ret;
}

template<class KEY, class VALUE, class LISTENER>
status_t ClientManager<KEY, VALUE, LISTENER>::waitUntilRemoved(
        const std::shared_ptr<ClientDescriptor<KEY, VALUE>> client,
        nsecs_t timeout) const {
    status_t ret = NO_ERROR;
    Mutex::Autolock lock(mLock);

    bool isRemoved = false;

    // Figure out what time in the future we should hit the timeout
    nsecs_t failTime = systemTime(SYSTEM_TIME_MONOTONIC) + timeout;

    while (!isRemoved) {
        isRemoved = true;
        for (const auto& i : mClients) {
            if (i == client) {
                isRemoved = false;
            }
        }

        if (!isRemoved) {
            ret = mRemovedCondition.waitRelative(mLock, timeout);
            if (ret != NO_ERROR) {
                break;
            }
            timeout = failTime - systemTime(SYSTEM_TIME_MONOTONIC);
        }
    }

    return ret;
}

template<class KEY, class VALUE, class LISTENER>
void ClientManager<KEY, VALUE, LISTENER>::setListener(const std::shared_ptr<LISTENER>& listener) {
    Mutex::Autolock lock(mLock);
    mListener = listener;
}

template<class KEY, class VALUE, class LISTENER>
void ClientManager<KEY, VALUE, LISTENER>::remove(
        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value) {
    Mutex::Autolock lock(mLock);
    // Remove evicted clients from list
    mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
        [this, &value] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
            if (curClientPtr == value) {
                if (mListener != nullptr) mListener->onClientRemoved(*curClientPtr);
                return true;
            }
            return false;
        }), mClients.end());
    mRemovedCondition.broadcast();
}

template<class KEY, class VALUE, class LISTENER>
int64_t ClientManager<KEY, VALUE, LISTENER>::getCurrentCostLocked() const {
    int64_t totalCost = 0;
    for (const auto& x : mClients) {
            totalCost += x->getCost();
    }
    return totalCost;
}

// --------------------------------------------------------------------------------

}; // namespace resource_policy
}; // namespace android

#endif // ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
