Tracks isolated uid's and their parent uid.

We push events from BatteryStatsImpl if an isolated uid is added or
removed and we have a custom rule in statsd to use these events to
update our uid map. In the future, we need to use this map to
convert all incoming uid's to their host uid.

Test: Added unit-test to UidMap_test.
Change-Id: I33c0451eb2c886161f22dd12e479d216fad0940d
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 8910523..f0689d2 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "Log.h"
+#include "statslog.h"
 
 #include "StatsLogProcessor.h"
 #include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
@@ -48,6 +49,22 @@
         pair.second->onLogEvent(msg);
         flushIfNecessary(msg.GetTimestampNs(), pair.first, pair.second);
     }
+
+    // Hard-coded logic to update the isolated uid's in the uid-map.
+    // The field numbers need to be currently updated by hand with stats_events.proto
+    if (msg.GetTagId() == android::util::ISOLATED_UID_CHANGED) {
+        status_t err = NO_ERROR, err2 = NO_ERROR, err3 = NO_ERROR;
+        bool is_create = msg.GetBool(3, &err);
+        auto parent_uid = int(msg.GetLong(1, &err2));
+        auto isolated_uid = int(msg.GetLong(2, &err3));
+        if (err == NO_ERROR && err2 == NO_ERROR && err3 == NO_ERROR) {
+            if (is_create) {
+                mUidMap->assignIsolatedUid(isolated_uid, parent_uid);
+            } else {
+                mUidMap->removeIsolatedUid(isolated_uid, parent_uid);
+            }
+        }
+    }
 }
 
 void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) {
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index d83c3a4..7b27642 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -150,6 +150,31 @@
     mSubscribers.erase(producer);
 }
 
+void UidMap::assignIsolatedUid(int isolatedUid, int parentUid) {
+    lock_guard<mutex> lock(mIsolatedMutex);
+
+    mIsolatedUidMap[isolatedUid] = parentUid;
+}
+
+void UidMap::removeIsolatedUid(int isolatedUid, int parentUid) {
+    lock_guard<mutex> lock(mIsolatedMutex);
+
+    auto it = mIsolatedUidMap.find(isolatedUid);
+    if (it != mIsolatedUidMap.end()) {
+        mIsolatedUidMap.erase(it);
+    }
+}
+
+int UidMap::getParentUidOrSelf(int uid) {
+    lock_guard<mutex> lock(mIsolatedMutex);
+
+    auto it = mIsolatedUidMap.find(uid);
+    if (it != mIsolatedUidMap.end()) {
+        return it->second;
+    }
+    return uid;
+}
+
 void UidMap::clearOutput() {
     mOutput.Clear();
 
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index bf120e0..de68fbc 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -84,6 +84,12 @@
     // Informs uid map that a config is removed. Used for keeping mConfigKeys up to date.
     void OnConfigRemoved(const ConfigKey& key);
 
+    void assignIsolatedUid(int isolatedUid, int parentUid);
+    void removeIsolatedUid(int isolatedUid, int parentUid);
+
+    // Returns the parent uid if it exists. Otherwise, returns the same uid that was passed-in.
+    int getParentUidOrSelf(int uid);
+
     // Gets the output. If every config key has received the output, then the output is cleared.
     UidMapping getOutput(const ConfigKey& key);
 
@@ -105,11 +111,16 @@
 
     // TODO: Use shared_mutex for improved read-locking if a library can be found in Android.
     mutable mutex mMutex;
+    mutable mutex mIsolatedMutex;
 
     // Maps uid to application data. This must be multimap since there is a feature in Android for
     // multiple apps to share the same uid.
     std::unordered_multimap<int, AppData> mMap;
 
+    // Maps isolated uid to the parent uid. Any metrics for an isolated uid will instead contribute
+    // to the parent uid.
+    std::unordered_map<int, int> mIsolatedUidMap;
+
     // We prepare the output proto as apps are updated, so that we can grab the current output.
     UidMapping mOutput;
 
diff --git a/cmds/statsd/src/stats_events.proto b/cmds/statsd/src/stats_events.proto
index 82d9759..8816795 100644
--- a/cmds/statsd/src/stats_events.proto
+++ b/cmds/statsd/src/stats_events.proto
@@ -72,7 +72,7 @@
         PhoneSignalStrengthChanged phone_signal_strength_changed = 40;
         SettingChanged setting_changed = 41;
         ActivityForegroundStateChanged activity_foreground_state_changed = 42;
-
+        IsolatedUidChanged isolated_uid_changed = 43;
         // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
     }
 
@@ -695,7 +695,6 @@
     optional int32 user = 7;
 }
 
-
 /*
  * Logs activity going to foreground or background
  *
@@ -848,3 +847,22 @@
     optional uint64 last_entry_timestamp_ms = 5;
     optional bool supported_only_in_suspend = 6;
 }
+
+/**
+ * Logs creation or removal of an isolated uid. Isolated uid's are temporary uid's to sandbox risky
+ * behavior in its own uid. However, the metrics of these isolated uid's almost always should be
+ * attributed back to the parent (host) uid. One example is Chrome.
+ *
+ * Logged from:
+ *   frameworks/base/core/java/com/android/internal/os/BatteryStatsImpl.java
+ */
+message IsolatedUidChanged {
+    // The host UID. Generally, we should attribute metrics from the isolated uid to the host uid.
+    optional int32 parent_uid = 1;
+
+    optional int32 isolated_uid = 2;
+
+    // 1 denotes we're creating an isolated uid and 0 denotes removal. We expect an isolated uid to
+    // be removed before if it's used for another parent uid.
+    optional int32 is_create = 3;
+}
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index 671f6d4..c64719e 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -13,7 +13,10 @@
 // limitations under the License.
 
 #include "packages/UidMap.h"
+#include "StatsLogProcessor.h"
 #include "config/ConfigKey.h"
+#include "logd/LogEvent.h"
+#include "statslog.h"
 
 #include <gtest/gtest.h>
 
@@ -29,6 +32,31 @@
 const string kApp1 = "app1.sharing.1";
 const string kApp2 = "app2.sharing.1";
 
+TEST(UidMapTest, TestIsolatedUID) {
+    sp<UidMap> m = new UidMap();
+    StatsLogProcessor p(m, nullptr);
+    LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1);
+    android_log_event_list* list = addEvent.GetAndroidLogEventList();
+    *list << 100;  // parent UID
+    *list << 101;  // isolated UID
+    *list << 1;    // Indicates creation.
+    addEvent.init();
+
+    EXPECT_EQ(101, m->getParentUidOrSelf(101));
+
+    p.OnLogEvent(addEvent);
+    EXPECT_EQ(100, m->getParentUidOrSelf(101));
+
+    LogEvent removeEvent(android::util::ISOLATED_UID_CHANGED, 1);
+    list = removeEvent.GetAndroidLogEventList();
+    *list << 100;  // parent UID
+    *list << 101;  // isolated UID
+    *list << 0;    // Indicates removal.
+    removeEvent.init();
+    p.OnLogEvent(removeEvent);
+    EXPECT_EQ(101, m->getParentUidOrSelf(101));
+}
+
 TEST(UidMapTest, TestMatching) {
     UidMap m;
     vector<int32_t> uids;