Define range of GIDs for cached app data.

To support upcoming disk usage calculation optimizations, this change
creates a new GID for each app that will be used to mark its cached
data.  We're allocating these unique GIDs so that we can use
quotactl() to track cached data on a per-app basis.

This change also tightens up the implementation of both the cache
and shared GID calculation to ensure that they stay inside the valid
ranges, and includes tests to verify.

Test: builds, boots, tests pass
Bug: 27948817
Change-Id: Ie4377e5aae267f2da39a165888139228995987cb
diff --git a/include/cutils/multiuser.h b/include/cutils/multiuser.h
index 7e7f815..4f23776 100644
--- a/include/cutils/multiuser.h
+++ b/include/cutils/multiuser.h
@@ -23,19 +23,19 @@
 extern "C" {
 #endif
 
-// NOTE: keep in sync with android.os.UserId
-
-#define MULTIUSER_APP_PER_USER_RANGE 100000
-#define MULTIUSER_FIRST_SHARED_APPLICATION_GID 50000
-#define MULTIUSER_FIRST_APPLICATION_UID 10000
-
 typedef uid_t userid_t;
 typedef uid_t appid_t;
 
 extern userid_t multiuser_get_user_id(uid_t uid);
 extern appid_t multiuser_get_app_id(uid_t uid);
-extern uid_t multiuser_get_uid(userid_t userId, appid_t appId);
-extern appid_t multiuser_get_shared_app_gid(uid_t uid);
+
+extern uid_t multiuser_get_uid(userid_t user_id, appid_t app_id);
+
+extern gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id);
+extern gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id);
+
+/* TODO: switch callers over to multiuser_get_shared_gid() */
+extern gid_t multiuser_get_shared_app_gid(uid_t uid);
 
 #ifdef __cplusplus
 }
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index 3031562..f7cf9b8 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -157,15 +157,21 @@
 #define AID_MISC          9998  /* access to misc storage */
 #define AID_NOBODY        9999
 
-#define AID_APP          10000  /* first app user */
+#define AID_APP              10000 /* TODO: switch users over to AID_APP_START */
+#define AID_APP_START        10000 /* first app user */
+#define AID_APP_END          19999 /* last app user */
 
-#define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
-#define AID_ISOLATED_END   99999 /* end of uids for fully isolated sandboxed processes */
-
-#define AID_USER        100000  /* offset for uid ranges for each user */
+#define AID_CACHE_GID_START  20000 /* start of gids for apps to mark cached data */
+#define AID_CACHE_GID_END    29999 /* end of gids for apps to mark cached data */
 
 #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
-#define AID_SHARED_GID_END   59999 /* start of gids for apps in each user to share */
+#define AID_SHARED_GID_END   59999 /* end of gids for apps in each user to share */
+
+#define AID_ISOLATED_START   99000 /* start of uids for fully isolated sandboxed processes */
+#define AID_ISOLATED_END     99999 /* end of uids for fully isolated sandboxed processes */
+
+#define AID_USER            100000 /* TODO: switch users over to AID_USER_OFFSET */
+#define AID_USER_OFFSET     100000 /* offset for uid ranges for each user */
 
 #if !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
 /*
diff --git a/libcutils/multiuser.c b/libcutils/multiuser.c
index 0f4427b..0ef337d 100644
--- a/libcutils/multiuser.c
+++ b/libcutils/multiuser.c
@@ -15,21 +15,36 @@
  */
 
 #include <cutils/multiuser.h>
+#include <private/android_filesystem_config.h>
 
 userid_t multiuser_get_user_id(uid_t uid) {
-    return uid / MULTIUSER_APP_PER_USER_RANGE;
+    return uid / AID_USER_OFFSET;
 }
 
 appid_t multiuser_get_app_id(uid_t uid) {
-    return uid % MULTIUSER_APP_PER_USER_RANGE;
+    return uid % AID_USER_OFFSET;
 }
 
-uid_t multiuser_get_uid(userid_t userId, appid_t appId) {
-    return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE);
+uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) {
+    return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
 }
 
-appid_t multiuser_get_shared_app_gid(uid_t id) {
-  return MULTIUSER_FIRST_SHARED_APPLICATION_GID + (id % MULTIUSER_APP_PER_USER_RANGE)
-          - MULTIUSER_FIRST_APPLICATION_UID;
+gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) {
+    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START);
+    } else {
+        return -1;
+    }
+}
 
+gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id) {
+    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_SHARED_GID_START);
+    } else {
+        return -1;
+    }
+}
+
+gid_t multiuser_get_shared_app_gid(uid_t uid) {
+    return multiuser_get_shared_gid(multiuser_get_user_id(uid), multiuser_get_app_id(uid));
 }
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
index abe3c21..0b0dc09 100644
--- a/libcutils/tests/Android.bp
+++ b/libcutils/tests/Android.bp
@@ -26,7 +26,8 @@
                 "trace-dev_test.cpp",
                 "test_str_parms.cpp",
                 "android_get_control_socket_test.cpp",
-                "android_get_control_file_test.cpp"
+                "android_get_control_file_test.cpp",
+                "multiuser_test.cpp"
             ],
         },
 
diff --git a/libcutils/tests/multiuser_test.cpp b/libcutils/tests/multiuser_test.cpp
new file mode 100644
index 0000000..2307ea8
--- /dev/null
+++ b/libcutils/tests/multiuser_test.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include <cutils/multiuser.h>
+#include <gtest/gtest.h>
+
+TEST(MultiuserTest, TestMerge) {
+    EXPECT_EQ(0, multiuser_get_uid(0, 0));
+    EXPECT_EQ(1000, multiuser_get_uid(0, 1000));
+    EXPECT_EQ(10000, multiuser_get_uid(0, 10000));
+    EXPECT_EQ(50000, multiuser_get_uid(0, 50000));
+    EXPECT_EQ(1000000, multiuser_get_uid(10, 0));
+    EXPECT_EQ(1001000, multiuser_get_uid(10, 1000));
+    EXPECT_EQ(1010000, multiuser_get_uid(10, 10000));
+    EXPECT_EQ(1050000, multiuser_get_uid(10, 50000));
+}
+
+TEST(MultiuserTest, TestSplitUser) {
+    EXPECT_EQ(0, multiuser_get_user_id(0));
+    EXPECT_EQ(0, multiuser_get_user_id(1000));
+    EXPECT_EQ(0, multiuser_get_user_id(10000));
+    EXPECT_EQ(0, multiuser_get_user_id(50000));
+    EXPECT_EQ(10, multiuser_get_user_id(1000000));
+    EXPECT_EQ(10, multiuser_get_user_id(1001000));
+    EXPECT_EQ(10, multiuser_get_user_id(1010000));
+    EXPECT_EQ(10, multiuser_get_user_id(1050000));
+}
+
+TEST(MultiuserTest, TestSplitApp) {
+    EXPECT_EQ(0, multiuser_get_app_id(0));
+    EXPECT_EQ(1000, multiuser_get_app_id(1000));
+    EXPECT_EQ(10000, multiuser_get_app_id(10000));
+    EXPECT_EQ(50000, multiuser_get_app_id(50000));
+    EXPECT_EQ(0, multiuser_get_app_id(1000000));
+    EXPECT_EQ(1000, multiuser_get_app_id(1001000));
+    EXPECT_EQ(10000, multiuser_get_app_id(1010000));
+    EXPECT_EQ(50000, multiuser_get_app_id(1050000));
+}
+
+TEST(MultiuserTest, TestCache) {
+    EXPECT_EQ(-1, multiuser_get_cache_gid(0, 0));
+    EXPECT_EQ(-1, multiuser_get_cache_gid(0, 1000));
+    EXPECT_EQ(20000, multiuser_get_cache_gid(0, 10000));
+    EXPECT_EQ(-1, multiuser_get_cache_gid(0, 50000));
+    EXPECT_EQ(1020000, multiuser_get_cache_gid(10, 10000));
+}
+
+TEST(MultiuserTest, TestShared) {
+    EXPECT_EQ(-1, multiuser_get_shared_gid(0, 0));
+    EXPECT_EQ(-1, multiuser_get_shared_gid(0, 1000));
+    EXPECT_EQ(50000, multiuser_get_shared_gid(0, 10000));
+    EXPECT_EQ(-1, multiuser_get_shared_gid(0, 50000));
+    EXPECT_EQ(1050000, multiuser_get_shared_gid(10, 10000));
+}
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index bab87ba..3c8bd75 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -182,7 +182,7 @@
     }
 
     // Parse /data/system/packages.list
-    uid_t userId = uid % AID_USER;
+    uid_t userId = uid % AID_USER_OFFSET;
     const char *name = android::uidToName(userId);
     if (!name && (userId > (AID_SHARED_GID_START - AID_APP))) {
         name = android::uidToName(userId - (AID_SHARED_GID_START - AID_APP));
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index aec51f4..e7b8cc2 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -165,12 +165,12 @@
   if (setegid(old_egid) == -1) error(1, errno, "couldn't restore egid");
 
   // Verify that user id is not too big.
-  if ((UID_MAX - info.uid) / AID_USER < (uid_t)userId) {
+  if ((UID_MAX - info.uid) / AID_USER_OFFSET < (uid_t)userId) {
     error(1, 0, "user id too big: %d", userId);
   }
 
   // Calculate user app ID.
-  uid_t userAppId = (AID_USER * userId) + info.uid;
+  uid_t userAppId = (AID_USER_OFFSET * userId) + info.uid;
 
   // Reject system packages.
   if (userAppId < AID_APP) {