Support /product-services partition

This CL is largely an adaptation of Change-Id
I16175933cebd9ec665d190cc5d564b5414a91827 . I also used the same way for
testing the change.

This CL will support the followings.
- installing a RRO package for framework from /product-services/overlay
- installing apps from /product-services/app
- installing priv-apps from /product-services/priv-app
- installing permissions from
  /product-services/etc/[default-permissions|permissions|sysconfig]

Bug: 80741439
Test: `mm` under frameworks/base/tests/[libs|privapp]-permissions
  adb sync && adb reboot
  adb shell cmd package list libraries
    => confirmed com.android.test.libs.product_services library
  adb shell cmd package dump \
    com.android.framework.permission.privapp.tests.product_services
    => confirmed that the package is a priv-app

  And I moved vendor/overlay/framework-res__auto_generated_rro.apk
  into system/product-services/overlay/ on taimen, and I confirmed that the
  RRO was installed properly.

Change-Id: I7a6a30bf8e8db9f2738594d187bb9148f138b8da
(cherry picked from commit a4af41736894bd3bf5bdc2a279acbeed2a24dd3d)
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 83e287a..b7a5352 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -615,6 +615,13 @@
     public static final int PRIVATE_FLAG_PRODUCT = 1 << 19;
 
     /**
+     * Value for {@link #privateFlags}: whether this app is pre-installed on the
+     * google partition of the system image.
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_PRODUCT_SERVICES = 1 << 21;
+
+    /**
      * Value for {@link #privateFlags}: whether this app is signed with the
      * platform key.
      * @hide
@@ -639,6 +646,7 @@
             PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE,
             PRIVATE_FLAG_PRIVILEGED,
             PRIVATE_FLAG_PRODUCT,
+            PRIVATE_FLAG_PRODUCT_SERVICES,
             PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER,
             PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY,
             PRIVATE_FLAG_STATIC_SHARED_LIBRARY,
@@ -1888,6 +1896,11 @@
         return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT) != 0;
     }
 
+    /** @hide */
+    public boolean isProductServices() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_PRODUCT_SERVICES) != 0;
+    }
+
     /**
      * Returns whether or not this application was installed as a virtual preload.
      */
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 876cf2b..83757c4 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -6785,6 +6785,11 @@
         }
 
         /** @hide */
+        public boolean isProductServices() {
+            return applicationInfo.isProductServices();
+        }
+
+        /** @hide */
         public boolean isPrivileged() {
             return applicationInfo.isPrivilegedApp();
         }
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index e32ed9d..347f60f 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -45,6 +45,7 @@
     private static final String ENV_ODM_ROOT = "ODM_ROOT";
     private static final String ENV_VENDOR_ROOT = "VENDOR_ROOT";
     private static final String ENV_PRODUCT_ROOT = "PRODUCT_ROOT";
+    private static final String ENV_PRODUCT_SERVICES_ROOT = "PRODUCT_SERVICES_ROOT";
 
     /** {@hide} */
     public static final String DIR_ANDROID = "Android";
@@ -67,6 +68,8 @@
     private static final File DIR_ODM_ROOT = getDirectory(ENV_ODM_ROOT, "/odm");
     private static final File DIR_VENDOR_ROOT = getDirectory(ENV_VENDOR_ROOT, "/vendor");
     private static final File DIR_PRODUCT_ROOT = getDirectory(ENV_PRODUCT_ROOT, "/product");
+    private static final File DIR_PRODUCT_SERVICES_ROOT = getDirectory(ENV_PRODUCT_SERVICES_ROOT,
+                                                           "/product_services");
 
     private static UserEnvironment sCurrentUser;
     private static boolean sUserRequired;
@@ -196,6 +199,16 @@
     }
 
     /**
+     * Return root directory of the "product_services" partition holding middleware
+     * services if any. If present, the partition is mounted read-only.
+     *
+     * @hide
+     */
+    public static File getProductServicesDirectory() {
+        return DIR_PRODUCT_SERVICES_ROOT;
+    }
+
+    /**
      * Return the system directory for a user. This is for use by system
      * services to store files relating to the user. This directory will be
      * automatically deleted when the user is removed.
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index c5be8e4..0a787b9 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -169,6 +169,10 @@
     final ArrayMap<String, ArraySet<String>> mProductPrivAppPermissions = new ArrayMap<>();
     final ArrayMap<String, ArraySet<String>> mProductPrivAppDenyPermissions = new ArrayMap<>();
 
+    final ArrayMap<String, ArraySet<String>> mProductServicesPrivAppPermissions = new ArrayMap<>();
+    final ArrayMap<String, ArraySet<String>> mProductServicesPrivAppDenyPermissions =
+            new ArrayMap<>();
+
     final ArrayMap<String, ArrayMap<String, Boolean>> mOemPermissions = new ArrayMap<>();
 
     public static SystemConfig getInstance() {
@@ -276,6 +280,14 @@
         return mProductPrivAppDenyPermissions.get(packageName);
     }
 
+    public ArraySet<String> getProductServicesPrivAppPermissions(String packageName) {
+        return mProductServicesPrivAppPermissions.get(packageName);
+    }
+
+    public ArraySet<String> getProductServicesPrivAppDenyPermissions(String packageName) {
+        return mProductServicesPrivAppDenyPermissions.get(packageName);
+    }
+
     public Map<String, Boolean> getOemPermissions(String packageName) {
         final Map<String, Boolean> oemPermissions = mOemPermissions.get(packageName);
         if (oemPermissions != null) {
@@ -326,6 +338,17 @@
                 Environment.getProductDirectory(), "etc", "sysconfig"), productPermissionFlag);
         readPermissions(Environment.buildPath(
                 Environment.getProductDirectory(), "etc", "permissions"), productPermissionFlag);
+
+        // Allow /product_services to customize system configs around libs, features, permissions
+        // and apps.
+        int productServicesPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PERMISSIONS |
+                ALLOW_APP_CONFIGS | ALLOW_PRIVAPP_PERMISSIONS;
+        readPermissions(Environment.buildPath(
+                Environment.getProductServicesDirectory(), "etc", "sysconfig"),
+                productServicesPermissionFlag);
+        readPermissions(Environment.buildPath(
+                Environment.getProductServicesDirectory(), "etc", "permissions"),
+                productServicesPermissionFlag);
     }
 
     void readPermissions(File libraryDir, int permissionFlag) {
@@ -659,22 +682,27 @@
                     }
                     XmlUtils.skipCurrentTag(parser);
                 } else if ("privapp-permissions".equals(name) && allowPrivappPermissions) {
-                    // privapp permissions from system, vendor and product partitions are stored
-                    // separately. This is to prevent xml files in the vendor partition from
-                    // granting permissions to priv apps in the system partition and vice
-                    // versa.
+                    // privapp permissions from system, vendor, product and product_services
+                    // partitions are stored separately. This is to prevent xml files in the vendor
+                    // partition from granting permissions to priv apps in the system partition and
+                    // vice versa.
                     boolean vendor = permFile.toPath().startsWith(
-                            Environment.getVendorDirectory().toPath())
+                            Environment.getVendorDirectory().toPath() + "/")
                             || permFile.toPath().startsWith(
-                                Environment.getOdmDirectory().toPath());
+                                Environment.getOdmDirectory().toPath() + "/");
                     boolean product = permFile.toPath().startsWith(
-                            Environment.getProductDirectory().toPath());
+                            Environment.getProductDirectory().toPath() + "/");
+                    boolean productServices = permFile.toPath().startsWith(
+                            Environment.getProductServicesDirectory().toPath() + "/");
                     if (vendor) {
                         readPrivAppPermissions(parser, mVendorPrivAppPermissions,
                                 mVendorPrivAppDenyPermissions);
                     } else if (product) {
                         readPrivAppPermissions(parser, mProductPrivAppPermissions,
                                 mProductPrivAppDenyPermissions);
+                    } else if (productServices) {
+                        readPrivAppPermissions(parser, mProductServicesPrivAppPermissions,
+                                mProductServicesPrivAppDenyPermissions);
                     } else {
                         readPrivAppPermissions(parser, mPrivAppPermissions,
                                 mPrivAppDenyPermissions);