resourcemanager: use std::vector for ResourceList

Encapsulate Resource List as vector of resources instead of map.
Since the number of resource is very limited, maintaining it as
std::vector helps with both performance and memory requiremnts.

Bug: 289097671
Test: atest android.media.misc.cts.ResourceManagerTest
      atest android.media.misc.cts.ResourceManagerMultiTest
      /data/nativetest64/ResourceManagerService_test/ResourceManagerService_test
      /data/nativetest64/ResourceObserverService_test/ResourceObserverService_test
Change-Id: Iec2ead5be1619c7db6d6c978f7398459ce59613b
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index 2bcfe27..3a79fa0 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -74,10 +74,7 @@
 
             const ResourceList& resources = info.resources;
             resourceLog.append("        Resources:\n");
-            for (auto it = resources.begin(); it != resources.end(); it++) {
-                snprintf(buffer, SIZE, "          %s\n", toString(it->second).c_str());
-                resourceLog.append(buffer);
-            }
+            resourceLog.append(resources.toString());
         }
     }
 
@@ -315,31 +312,21 @@
 
     for (size_t i = 0; i < resources.size(); ++i) {
         const auto &res = resources[i];
-        const auto resType = std::tuple(res.type, res.subType, res.id);
 
         if (res.value < 0 && res.type != MediaResource::Type::kDrmSession) {
             ALOGW("Ignoring request to remove negative value of non-drm resource");
             continue;
         }
-        if (info.resources.find(resType) == info.resources.end()) {
-            if (res.value <= 0) {
-                // We can't init a new entry with negative value, although it's allowed
-                // to merge in negative values after the initial add.
-                ALOGW("Ignoring request to add new resource entry with value <= 0");
-                continue;
-            }
+        bool isNewEntry = false;
+        if (!info.resources.add(res, &isNewEntry)) {
+            continue;
+        }
+        if (isNewEntry) {
             onFirstAdded(res, info.uid);
-            info.resources[resType] = res;
-        } else {
-            mergeResources(info.resources[resType], res);
         }
+
         // Add it to the list of added resources for observers.
-        auto it = resourceAdded.find(resType);
-        if (it == resourceAdded.end()) {
-            resourceAdded[resType] = res;
-        } else {
-            mergeResources(it->second, res);
-        }
+        resourceAdded.add(res);
     }
     if (info.deathNotifier == nullptr && client != nullptr) {
         info.deathNotifier = DeathNotifier::Create(
@@ -386,31 +373,22 @@
     ResourceList resourceRemoved;
     for (size_t i = 0; i < resources.size(); ++i) {
         const auto &res = resources[i];
-        const auto resType = std::tuple(res.type, res.subType, res.id);
 
         if (res.value < 0) {
             ALOGW("Ignoring request to remove negative value of resource");
             continue;
         }
-        // ignore if we don't have it
-        if (info.resources.find(resType) != info.resources.end()) {
-            MediaResourceParcel &resource = info.resources[resType];
+
+        long removedEntryValue = -1;
+        if (info.resources.remove(res, &removedEntryValue)) {
             MediaResourceParcel actualRemoved = res;
-            if (resource.value > res.value) {
-                resource.value -= res.value;
-            } else {
+            if (removedEntryValue != -1) {
                 onLastRemoved(res, info.uid);
-                actualRemoved.value = resource.value;
-                info.resources.erase(resType);
+                actualRemoved.value = removedEntryValue;
             }
 
             // Add it to the list of removed resources for observers.
-            auto it = resourceRemoved.find(resType);
-            if (it == resourceRemoved.end()) {
-                resourceRemoved[resType] = actualRemoved;
-            } else {
-                mergeResources(it->second, actualRemoved);
-            }
+            resourceRemoved.add(actualRemoved);
         }
     }
     if (mObserverService != nullptr && !resourceRemoved.empty()) {
@@ -453,8 +431,8 @@
     }
 
     const ResourceInfo& info = foundClient->second;
-    for (auto it = info.resources.begin(); it != info.resources.end(); it++) {
-        onLastRemoved(it->second, info.uid);
+    for (const MediaResourceParcel& res : info.resources.getResources()) {
+        onLastRemoved(res, info.uid);
     }
 
     // Since this client has been removed, update the metrics collector.
@@ -1056,8 +1034,7 @@
         if (pendingRemovalOnly && !info.pendingRemoval) {
             continue;
         }
-        for (auto it = resources.begin(); it != resources.end(); it++) {
-            const MediaResourceParcel &resource = it->second;
+        for (const MediaResourceParcel& resource : resources.getResources()) {
             if (hasResourceType(type, subType, resource)) {
                 if (resource.value > largestValue) {
                     largestValue = resource.value;
diff --git a/services/mediaresourcemanager/ResourceManagerServiceUtils.cpp b/services/mediaresourcemanager/ResourceManagerServiceUtils.cpp
index 4d6184d..679ab13 100644
--- a/services/mediaresourcemanager/ResourceManagerServiceUtils.cpp
+++ b/services/mediaresourcemanager/ResourceManagerServiceUtils.cpp
@@ -27,6 +27,93 @@
 
 namespace android {
 
+bool ResourceList::add(const MediaResourceParcel& res, bool* isNewEntry) {
+    // See if it's an existing entry, if so, merge it.
+    for (MediaResourceParcel& item : mResourceList) {
+        if (item.type == res.type && item.subType == res.subType && item.id == res.id) {
+            // We already have an item. Merge them and return
+            mergeResources(item, res);
+            return true;
+        }
+    }
+
+    // Since we have't found this resource yet, it is a new entry.
+    // We can't init a new entry with negative value, although it's allowed
+    // to merge in negative values after the initial add.
+    if (res.value <= 0) {
+        ALOGW("Ignoring request to add new resource entry with value <= 0");
+        return false;
+    }
+    if (isNewEntry) {
+        *isNewEntry = true;
+    }
+    mResourceList.push_back(res);
+    return true;
+}
+
+void ResourceList::addOrUpdate(const MediaResourceParcel& res) {
+    // See if it's an existing entry, just update the value.
+    for (MediaResourceParcel& item : mResourceList) {
+        if (item.type == res.type && item.subType == res.subType && item.id == res.id) {
+            item.value = res.value;
+            return;
+        }
+    }
+
+    // Add the new entry.
+    mResourceList.push_back(res);
+}
+
+bool ResourceList::remove(const MediaResourceParcel& res, long* removedEntryValue) {
+    // Make sure we have an entry for this resource.
+    for (std::vector<MediaResourceParcel>::iterator it = mResourceList.begin();
+         it != mResourceList.end(); it++) {
+        if (it->type == res.type && it->subType == res.subType && it->id == res.id) {
+            if (it->value > res.value) {
+                // Subtract the resource value by given value.
+                it->value -= res.value;
+            } else {
+                // This entry will be removed.
+                if (removedEntryValue) {
+                    *removedEntryValue = it->value;
+                }
+                mResourceList.erase(it);
+            }
+            return true;
+        }
+    }
+
+    // No such entry.
+    return false;
+}
+
+std::string ResourceList::toString() const {
+    std::string str;
+    for (const ::aidl::android::media::MediaResourceParcel& res : mResourceList) {
+        str.append(android::toString(res).c_str());
+        str.append("\n");
+    }
+
+    return std::move(str);
+}
+
+bool ResourceList::operator==(const ResourceList& rhs) const {
+    // Make sure the size is the same.
+    if (mResourceList.size() != rhs.mResourceList.size()) {
+        return false;
+    }
+
+    // Create a set from this object and check for the items from the rhs.
+    std::set<::aidl::android::media::MediaResourceParcel> lhs(
+            mResourceList.begin(), mResourceList.end());
+    for (const ::aidl::android::media::MediaResourceParcel& res : rhs.mResourceList) {
+        if (lhs.find(res) == lhs.end()) {
+            return false;
+        }
+    }
+    return true;
+}
+
 // Bunch of utility functions that looks for a specific Resource.
 // Check whether a given resource (of type and subtype) is found in given resource parcel.
 bool hasResourceType(MediaResource::Type type, MediaResource::SubType subType,
@@ -53,8 +140,8 @@
 // Check whether a given resource (of type and subtype) is found in given resource list.
 bool hasResourceType(MediaResource::Type type, MediaResource::SubType subType,
                      const ResourceList& resources) {
-    for (auto it = resources.begin(); it != resources.end(); it++) {
-        if (hasResourceType(type, subType, it->second)) {
+    for (const MediaResourceParcel& res : resources.getResources()) {
+        if (hasResourceType(type, subType, res)) {
             return true;
         }
     }
diff --git a/services/mediaresourcemanager/ResourceManagerServiceUtils.h b/services/mediaresourcemanager/ResourceManagerServiceUtils.h
index 7e336e3..32cb219 100644
--- a/services/mediaresourcemanager/ResourceManagerServiceUtils.h
+++ b/services/mediaresourcemanager/ResourceManagerServiceUtils.h
@@ -19,6 +19,7 @@
 #define ANDROID_MEDIA_RESOURCEMANAGERSERVICEUTILS_H_
 
 #include <map>
+#include <set>
 #include <memory>
 #include <vector>
 
@@ -99,10 +100,47 @@
     virtual void binderDied();
 };
 
-// A map of tuple(type, sub-type, id) and the resource parcel.
-typedef std::map<std::tuple<
-        MediaResource::Type, MediaResource::SubType, std::vector<uint8_t>>,
-        ::aidl::android::media::MediaResourceParcel> ResourceList;
+// Encapsulate Resource List as vector of resources instead of map.
+// Since the number of resource is very limited, maintaining it as
+// std::vector helps with both performance and memory requiremnts.
+struct ResourceList {
+    // Add or Update an entry into ResourceList.
+    // If a new entry is added, isNewEntry will be set to true upon return
+    // returns true on successful update, false otherwise.
+    bool add(const ::aidl::android::media::MediaResourceParcel& res, bool* isNewEntry = nullptr);
+
+    // reduce the resource usage by subtracting the resource value.
+    // If the resource value is 0 after reducing the resource usage,
+    // that entry will be removed and removedEntryValue is set to the
+    // value before it was removed upon return otherwise it will be set to -1.
+    // returns true on successful removal of the resource, false otherwise.
+    bool remove(const ::aidl::android::media::MediaResourceParcel& res,
+                long* removedEntryValue = nullptr);
+
+    // Returns true if there aren't any resource entries.
+    bool empty() const {
+        return mResourceList.empty();
+    }
+
+    // Returns resource list as a non-modifiable vectors
+    const std::vector<::aidl::android::media::MediaResourceParcel>& getResources() const {
+        return mResourceList;
+    }
+
+    // Converts resource list into string format
+    std::string toString() const;
+
+    // BEGIN: Test only function
+    // Check if two resource lists are the same.
+    bool operator==(const ResourceList& rhs) const;
+
+    // Add or Update an entry into ResourceList.
+    void addOrUpdate(const ::aidl::android::media::MediaResourceParcel& res);
+    // END: Test only function
+
+private:
+    std::vector<::aidl::android::media::MediaResourceParcel> mResourceList;
+};
 
 // Encapsulation for Resource Info, that contains
 // - pid of the app
diff --git a/services/mediaresourcemanager/ResourceObserverService.cpp b/services/mediaresourcemanager/ResourceObserverService.cpp
index 72e249f..21e61e9 100644
--- a/services/mediaresourcemanager/ResourceObserverService.cpp
+++ b/services/mediaresourcemanager/ResourceObserverService.cpp
@@ -286,9 +286,9 @@
     {
         std::scoped_lock lock{mObserverLock};
 
-        for (auto &res : resources) {
+        for (const MediaResourceParcel& res : resources.getResources()) {
             // Skip if this resource doesn't map to any observable type.
-            MediaObservableType observableType = getObservableType(res.second);
+            MediaObservableType observableType = getObservableType(res);
             if (observableType == MediaObservableType::kInvalid) {
                 continue;
             }
@@ -303,9 +303,9 @@
                 auto calleeIt = calleeList.find(subscriber.first);
                 if (calleeIt == calleeList.end()) {
                     calleeList.emplace(subscriber.first, CalleeInfo{
-                        subscriber.second, {{observableType, res.second.value}}});
+                        subscriber.second, {{observableType, res.value}}});
                 } else {
-                    calleeIt->second.monitors.push_back({observableType, res.second.value});
+                    calleeIt->second.monitors.push_back({observableType, res.value});
                 }
             }
         }
diff --git a/services/mediaresourcemanager/ResourceTracker.cpp b/services/mediaresourcemanager/ResourceTracker.cpp
index 3ec3dc8..22381c3 100644
--- a/services/mediaresourcemanager/ResourceTracker.cpp
+++ b/services/mediaresourcemanager/ResourceTracker.cpp
@@ -39,12 +39,12 @@
     bool foundResource = false;
     bool matchedPrimary =
         (primarySubType == MediaResource::SubType::kUnspecifiedSubType) ?  true : false;
-    for (auto it = resources.begin(); it != resources.end(); it++) {
-        if (hasResourceType(type, subType, it->second)) {
+    for (const MediaResourceParcel& res : resources.getResources()) {
+        if (hasResourceType(type, subType, res)) {
             foundResource = true;
-        } else if (it->second.subType == primarySubType) {
+        } else if (res.subType == primarySubType) {
             matchedPrimary = true;
-        } else if (isHwCodec(it->second.subType) == isHwCodec(primarySubType)) {
+        } else if (isHwCodec(res.subType) == isHwCodec(primarySubType)) {
             matchedPrimary = true;
         }
         if (matchedPrimary && foundResource) {
@@ -111,31 +111,20 @@
     ResourceList resourceAdded;
 
     for (const MediaResourceParcel& res : resources) {
-        const auto resType = std::tuple(res.type, res.subType, res.id);
-
         if (res.value < 0 && res.type != MediaResource::Type::kDrmSession) {
             ALOGV("%s: Ignoring request to remove negative value of non-drm resource", __func__);
             continue;
         }
-        if (info.resources.find(resType) == info.resources.end()) {
-            if (res.value <= 0) {
-                // We can't init a new entry with negative value, although it's allowed
-                // to merge in negative values after the initial add.
-                ALOGV("%s: Ignoring request to add new resource entry with value <= 0", __func__);
-                continue;
-            }
+        bool isNewEntry = false;
+        if (!info.resources.add(res, &isNewEntry)) {
+            continue;
+        }
+        if (isNewEntry) {
             onFirstAdded(res, info.uid);
-            info.resources[resType] = res;
-        } else {
-            mergeResources(info.resources[resType], res);
         }
+
         // Add it to the list of added resources for observers.
-        auto it = resourceAdded.find(resType);
-        if (it == resourceAdded.end()) {
-            resourceAdded[resType] = res;
-        } else {
-            mergeResources(it->second, res);
-        }
+        resourceAdded.add(res);
     }
     if (info.deathNotifier == nullptr && client != nullptr) {
         info.deathNotifier = DeathNotifier::Create(client, mService, clientInfo);
@@ -186,31 +175,21 @@
     ResourceInfo& info = foundClient->second;
     ResourceList resourceRemoved;
     for (const MediaResourceParcel& res : resources) {
-        const auto resType = std::tuple(res.type, res.subType, res.id);
-
         if (res.value < 0) {
             ALOGV("%s: Ignoring request to remove negative value of resource", __func__);
             continue;
         }
-        // ignore if we don't have it
-        if (info.resources.find(resType) != info.resources.end()) {
-            MediaResourceParcel& resource = info.resources[resType];
+
+        long removedEntryValue = -1;
+        if (info.resources.remove(res, &removedEntryValue)) {
             MediaResourceParcel actualRemoved = res;
-            if (resource.value > res.value) {
-                resource.value -= res.value;
-            } else {
+            if (removedEntryValue != -1) {
                 onLastRemoved(res, info.uid);
-                actualRemoved.value = resource.value;
-                info.resources.erase(resType);
+                actualRemoved.value = removedEntryValue;
             }
 
             // Add it to the list of removed resources for observers.
-            auto it = resourceRemoved.find(resType);
-            if (it == resourceRemoved.end()) {
-                resourceRemoved[resType] = actualRemoved;
-            } else {
-                mergeResources(it->second, actualRemoved);
-            }
+            resourceRemoved.add(actualRemoved);
         }
     }
     if (mObserverService != nullptr && !resourceRemoved.empty()) {
@@ -243,8 +222,8 @@
     }
 
     const ResourceInfo& info = foundClient->second;
-    for (auto& [resType, resParcel] : info.resources) {
-        onLastRemoved(resParcel, info.uid);
+    for (const MediaResourceParcel& res : info.resources.getResources()) {
+        onLastRemoved(res, info.uid);
     }
 
     if (mObserverService != nullptr && !info.resources.empty()) {
@@ -523,8 +502,7 @@
         if (!info.pendingRemoval) {
             continue;
         }
-        for (auto it = resources.begin(); it != resources.end(); it++) {
-            const MediaResourceParcel& resource = it->second;
+        for (const MediaResourceParcel& resource : resources.getResources()) {
             if (hasResourceType(type, subType, resource)) {
                 if (resource.value > largestValue) {
                     largestValue = resource.value;
@@ -567,19 +545,20 @@
         const ResourceList& resources = info->resources;
         bool matchedPrimary =
             (primarySubType == MediaResource::SubType::kUnspecifiedSubType) ?  true : false;
-        for (auto it = resources.begin(); !matchedPrimary && it != resources.end(); it++) {
-            if (it->second.subType == primarySubType) {
+        for (const MediaResourceParcel& resource : resources.getResources()) {
+            if (resource.subType == primarySubType) {
                 matchedPrimary = true;
-            } else if (isHwCodec(it->second.subType) == isHwCodec(primarySubType)) {
+                break;
+            } else if (isHwCodec(resource.subType) == isHwCodec(primarySubType)) {
                 matchedPrimary = true;
+                break;
             }
         }
         // Primary type doesn't match, skip the client
         if (!matchedPrimary) {
             continue;
         }
-        for (auto it = resources.begin(); it != resources.end(); it++) {
-            const MediaResourceParcel& resource = it->second;
+        for (const MediaResourceParcel& resource : resources.getResources()) {
             if (hasResourceType(type, subType, resource)) {
                 if (resource.value > largestValue) {
                     largestValue = resource.value;
@@ -629,10 +608,10 @@
         const ResourceList& resources = info->resources;
         bool matchedPrimary =
             (primarySubType == MediaResource::SubType::kUnspecifiedSubType) ?  true : false;
-        for (auto it = resources.begin(); !matchedPrimary && it != resources.end(); it++) {
-            if (it->second.subType == primarySubType) {
+        for (const MediaResourceParcel& resource : resources.getResources()) {
+            if (resource.subType == primarySubType) {
                 matchedPrimary = true;
-            } else if (isHwCodec(it->second.subType) == isHwCodec(primarySubType)) {
+            } else if (isHwCodec(resource.subType) == isHwCodec(primarySubType)) {
                 matchedPrimary = true;
             }
         }
@@ -640,8 +619,7 @@
         if (!matchedPrimary) {
             continue;
         }
-        for (auto it = resources.begin(); it != resources.end(); it++) {
-            const MediaResourceParcel& resource = it->second;
+        for (const MediaResourceParcel& resource : resources.getResources()) {
             if (hasResourceType(type, subType, resource)) {
                 if (resource.value > largestValue) {
                     largestValue = resource.value;
@@ -690,10 +668,7 @@
 
             const ResourceList& resources = info.resources;
             resourceLogs.append("        Resources:\n");
-            for (auto it = resources.begin(); it != resources.end(); it++) {
-                snprintf(buffer, SIZE, "          %s\n", toString(it->second).c_str());
-                resourceLogs.append(buffer);
-            }
+            resourceLogs.append(resources.toString());
         }
     }
     resourceLogs.append("  Process Pid override:\n");
diff --git a/services/mediaresourcemanager/test/ResourceManagerServiceTestUtils.h b/services/mediaresourcemanager/test/ResourceManagerServiceTestUtils.h
index aa3a944..7e8a4a0 100644
--- a/services/mediaresourcemanager/test/ResourceManagerServiceTestUtils.h
+++ b/services/mediaresourcemanager/test/ResourceManagerServiceTestUtils.h
@@ -245,9 +245,7 @@
         // convert resource1 to ResourceList
         ResourceList r1;
         for (size_t i = 0; i < resources1.size(); ++i) {
-            const auto &res = resources1[i];
-            const auto resType = std::tuple(res.type, res.subType, res.id);
-            r1[resType] = res;
+            r1.addOrUpdate(resources1[i]);
         }
         return r1 == resources2;
     }
diff --git a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
index 3f11d59..b3a0932 100644
--- a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
+++ b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
@@ -209,7 +209,6 @@
 
         resources1.clear();
         resources1.push_back(MediaResource(MediaResource::Type::kDrmSession, INT64_MIN));
-        expected.push_back(MediaResource(MediaResource::Type::kNonSecureCodec, INT64_MIN));
         mService->addResource(client1Info, mTestClient1, resources1);
 
         // Expected result: