Implement restricted metrics changed operation

Very generally follows the pattern of active configs/data fetch
broadcasts.

A few things are still todo:
  - tests
  - resending all pending intents on statsd restart
  - returning the current
  - code optimizations/cleanups

Test: m
Test: atest statsd_test
Bug: 268141944
Ignore-AOSP-First: U feature
Change-Id: I1f8c52c6dc0570630c92d29adb6ce8b14c08c3e7
diff --git a/statsd/src/config/ConfigManager.cpp b/statsd/src/config/ConfigManager.cpp
index 570cd45..3a9c69c 100644
--- a/statsd/src/config/ConfigManager.cpp
+++ b/statsd/src/config/ConfigManager.cpp
@@ -37,6 +37,8 @@
 using std::string;
 using std::vector;
 
+using Status = ::ndk::ScopedAStatus;
+
 #define STATS_SERVICE_DIR "/data/misc/stats-service"
 
 using android::base::StringPrintf;
@@ -152,6 +154,61 @@
     }
 }
 
+void ConfigManager::SetRestrictedMetricsChangedReceiver(const string& configPackage,
+                                                        const int64_t configId,
+                                                        const int32_t callingUid,
+                                                        const shared_ptr<IPendingIntentRef>& pir) {
+    lock_guard<mutex> lock(mMutex);
+    ConfigKeyWithPackage configKey(configPackage, configId);
+    mRestrictedMetricsChangedReceivers[configKey][callingUid] = pir;
+}
+
+void ConfigManager::RemoveRestrictedMetricsChangedReceiver(const string& configPackage,
+                                                           const int64_t configId,
+                                                           const int32_t callingUid) {
+    lock_guard<mutex> lock(mMutex);
+    ConfigKeyWithPackage configKey(configPackage, configId);
+    const auto& it = mRestrictedMetricsChangedReceivers.find(configKey);
+    if (it != mRestrictedMetricsChangedReceivers.end()) {
+        it->second.erase(callingUid);
+        if (it->second.empty()) {
+            mRestrictedMetricsChangedReceivers.erase(it);
+        }
+    }
+}
+
+void ConfigManager::SendRestrictedMetricsBroadcast(const set<string>& configPackages,
+                                                   const int64_t configId,
+                                                   const set<int32_t>& delegateUids,
+                                                   const vector<int64_t>& metricIds) {
+    set<shared_ptr<IPendingIntentRef>> intentsToSend;
+    {
+        lock_guard<mutex> lock(mMutex);
+        // Invoke the pending intent for all matching configs, as long as the listening delegates
+        // match the allowed delegate uids specified by the config.
+        for (const string& configPackage : configPackages) {
+            ConfigKeyWithPackage key(configPackage, configId);
+            const auto& it = mRestrictedMetricsChangedReceivers.find(key);
+            if (it != mRestrictedMetricsChangedReceivers.end()) {
+                for (const auto& [delegateUid, pir] : it->second) {
+                    if (delegateUids.find(delegateUid) != delegateUids.end()) {
+                        intentsToSend.insert(pir);
+                    }
+                }
+            }
+        }
+    }
+
+    // Invoke the pending intents without holding the lock.
+    for (const shared_ptr<IPendingIntentRef>& pir : intentsToSend) {
+        Status status = pir->sendRestrictedMetricsChangedBroadcast(metricIds);
+        if (status.isOk()) {
+            VLOG("ConfigManager::SendRestrictedMetricsBroadcast succeeded");
+        }
+        // TODO (b/269419485): handle failures.
+    }
+}
+
 void ConfigManager::RemoveConfig(const ConfigKey& key) {
     vector<sp<ConfigListener>> broadcastList;
     {
@@ -181,6 +238,7 @@
     StorageManager::deleteSuffixedFiles(STATS_SERVICE_DIR, suffix.c_str());
 }
 
+// TODO(b/xxx): consider removing all receivers associated with this uid.
 void ConfigManager::RemoveConfigs(int uid) {
     vector<ConfigKey> removed;
     vector<sp<ConfigListener>> broadcastList;