| /* |
| * 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 <algorithm> |
| #include <utility> |
| #include <vector> |
| #include <set> |
| #include <map> |
| #include <memory> |
| |
| 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); |
| 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); |
| |
| ~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); |
| |
| // 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; |
| }; // 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) : |
| mKey{key}, mValue{value}, mCost{cost}, mConflicting{conflictingKeys}, |
| mPriority(score, state, isVendorClient, scoreOffset), |
| mOwnerId{ownerId} {} |
| |
| 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) : |
| 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} {} |
| |
| 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 (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> |
| 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 and return the ClientDescriptor with a given key. |
| */ |
| std::shared_ptr<ClientDescriptor<KEY, VALUE>> remove(const KEY& key); |
| |
| /** |
| * Remove the given ClientDescriptor. |
| */ |
| 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; |
| |
| /** |
| * 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(); |
| |
| 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 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> |
| 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::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 |