Merge "[pm] a system config to let oems register uids" into main am: 715ffd2276

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/3211631

Change-Id: I62f53175edc8be3e4c0cdfdbd77d295589fe9698
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 050f9b5..8f85617 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -78,6 +78,12 @@
     src: "package-shareduid-allowlist.xml",
 }
 
+prebuilt_etc {
+    name: "oem-defined-uids.xml",
+    sub_dir: "sysconfig",
+    src: "oem-defined-uids.xml",
+}
+
 // Privapp permission whitelist files
 
 prebuilt_etc {
diff --git a/data/etc/oem-defined-uids.xml b/data/etc/oem-defined-uids.xml
new file mode 100644
index 0000000..87435b9
--- /dev/null
+++ b/data/etc/oem-defined-uids.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 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.
+ -->
+
+<!--
+This XML defines a list of UIDs for OEMs to register as shared UIDs. They will be registered at the
+start of the system, which allows OEMs to create services with these UIDs. The range of these UIDs
+must be in the OEM reserved range.
+
+OEM must provide a preloaded app that is installed at boot time to retain the newly registered UID
+by adding a android:sharedUserId tag in the manifest of the preloaded app, with the value of the tag
+set to the name of the UID defined in this config file. Otherwise, the uid will be cleared at the
+end of the boot and this config file will take no effect.
+
+- The "name" XML attribute refers to the name of the shared UID. It must start with "android.uid.".
+- The "uid" XML attribute refers to the value of the shared UID. It must be in range [2900, 2999].
+
+Example usage
+    <oem-defined-uid name="android.uid.vendordata" uid="2918"/>
+    Indicates that a shared UID named "android.uid.vendordata" will be added to the system with the
+    UID of 2918.
+-->
+
+<config>
+</config>
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index e2ab0d9..d80e40c 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -371,6 +371,10 @@
     // exempt from ECM (i.e., they will never be considered "restricted").
     private final ArraySet<SignedPackage> mEnhancedConfirmationTrustedInstallers = new ArraySet<>();
 
+    // A map of UIDs defined by OEMs, mapping from name to value. The UIDs will be registered at the
+    // start of the system which allows OEMs to create and register their system services.
+    @NonNull private final ArrayMap<String, Integer> mOemDefinedUids = new ArrayMap<>();
+
     /**
      * Map of system pre-defined, uniquely named actors; keys are namespace,
      * value maps actor name to package name.
@@ -594,6 +598,10 @@
         return mEnhancedConfirmationTrustedInstallers;
     }
 
+    @NonNull
+    public ArrayMap<String, Integer> getOemDefinedUids() {
+        return mOemDefinedUids;
+    }
     /**
      * Only use for testing. Do NOT use in production code.
      * @param readPermissions false to create an empty SystemConfig; true to read the permissions.
@@ -1628,6 +1636,26 @@
                             }
                         }
                     } break;
+                    case "oem-defined-uid": {
+                        final String uidName = parser.getAttributeValue(null, "name");
+                        final String uidValue = parser.getAttributeValue(null, "uid");
+                        if (TextUtils.isEmpty(uidName)) {
+                            Slog.w(TAG, "<" + name + "> without valid uid name in " + permFile
+                                    + " at " + parser.getPositionDescription());
+                        } else if (TextUtils.isEmpty(uidValue)) {
+                            Slog.w(TAG, "<" + name + "> without valid uid value in " + permFile
+                                    + " at " + parser.getPositionDescription());
+                        } else {
+                            try {
+                                final int oemDefinedUid = Integer.parseInt(uidValue);
+                                mOemDefinedUids.put(uidName, oemDefinedUid);
+                            } catch (NumberFormatException e) {
+                                Slog.w(TAG, "<" + name + "> with invalid uid value: "
+                                        + uidValue + " in " + permFile
+                                        + " at " + parser.getPositionDescription());
+                            }
+                        }
+                    } break;
                     case "enhanced-confirmation-trusted-package": {
                         if (android.permission.flags.Flags.enhancedConfirmationModeApisEnabled()) {
                             SignedPackage signedPackage = parseEnhancedConfirmationTrustedPackage(
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c0b8034..8279ea0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2033,6 +2033,10 @@
         // CHECKSTYLE:ON IndentationCheck
         t.traceEnd();
 
+        t.traceBegin("get system config");
+        SystemConfig systemConfig = injector.getSystemConfig();
+        t.traceEnd();
+
         t.traceBegin("addSharedUsers");
         mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
@@ -2052,6 +2056,13 @@
                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
         mSettings.addSharedUserLPw("android.uid.uwb", UWB_UID,
                 ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+        final ArrayMap<String, Integer> oemDefinedUids = systemConfig.getOemDefinedUids();
+        final int numOemDefinedUids = oemDefinedUids.size();
+        for (int i = 0; i < numOemDefinedUids; i++) {
+            mSettings.addOemSharedUserLPw(oemDefinedUids.keyAt(i), oemDefinedUids.valueAt(i),
+                    ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
+        }
+
         t.traceEnd();
 
         String separateProcesses = SystemProperties.get("debug.separate_processes");
@@ -2083,10 +2094,7 @@
         mContext.getSystemService(DisplayManager.class)
                 .getDisplay(Display.DEFAULT_DISPLAY).getMetrics(mMetrics);
 
-        t.traceBegin("get system config");
-        SystemConfig systemConfig = injector.getSystemConfig();
         mAvailableFeatures = systemConfig.getAvailableFeatures();
-        t.traceEnd();
 
         mProtectedPackages = new ProtectedPackages(mContext);
 
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 3956552..2eb9a38 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -976,6 +976,21 @@
         return null;
     }
 
+    SharedUserSetting addOemSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
+        if (!name.startsWith("android.uid")) {
+            PackageManagerService.reportSettingsProblem(Log.ERROR,
+                    "Failed to add oem defined shared user because of invalid name: " + name);
+            return null;
+        }
+        // OEM defined uids must be in the OEM reserved range
+        if (uid < 2900 || uid > 2999) {
+            PackageManagerService.reportSettingsProblem(Log.ERROR,
+                    "Failed to add oem defined shared user because of invalid uid: " + uid);
+            return null;
+        }
+        return addSharedUserLPw(name, uid, pkgFlags, pkgPrivateFlags);
+    }
+
     SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
         SharedUserSetting s = mSharedUsers.get(name);
         if (s != null) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 9ab607d..0a6edf1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -314,6 +314,7 @@
         whenever(mocks.systemConfig.defaultVrComponents).thenReturn(ArraySet())
         whenever(mocks.systemConfig.hiddenApiWhitelistedApps).thenReturn(ArraySet())
         whenever(mocks.systemConfig.appMetadataFilePaths).thenReturn(ArrayMap())
+        whenever(mocks.systemConfig.oemDefinedUids).thenReturn(ArrayMap())
         wheneverStatic { SystemProperties.set(anyString(), anyString()) }.thenDoNothing()
         wheneverStatic { SystemProperties.getBoolean("fw.free_cache_v2", true) }.thenReturn(true)
         wheneverStatic { Environment.getApexDirectory() }.thenReturn(apexDirectory)