Added support for dynamic RRO

Also added overlayable.xml file
Remaining task:
Convert overlay from seahwak, cuttlefish and emulator into static RRO.

Bug: 198516172
Test: Checked the following manually:
      1. config_car_bugreport_application value updated correctly for emulator for both user and user debug build
      2. userDebug build
         overlay package name: com.android.car.resources.vendor
         targetPackageName: com.android.car.updatable
      3. user build
         overlay package name: com.google.android.car.resources.vendor
         targetPackageName: com.google.android.car.updatable

Change-Id: Ifa70ba0249dd3c703051276572b851dde203d235
diff --git a/car-builtin-lib/api/module-lib-current.txt b/car-builtin-lib/api/module-lib-current.txt
index 5607976..91de6ab 100644
--- a/car-builtin-lib/api/module-lib-current.txt
+++ b/car-builtin-lib/api/module-lib-current.txt
@@ -78,7 +78,7 @@
     method public static boolean isUpdatedSystemApp(@NonNull android.content.pm.ApplicationInfo);
     method public static boolean isVendorApp(@NonNull android.content.pm.ApplicationInfo);
     method public static void setApplicationEnabledSettingForUser(@NonNull String, int, int, int, @NonNull String) throws android.os.RemoteException;
-    field public static final String PROPERTY_CAR_SERVICE_OVERLAY_PACKAGE_NAME = "ro.android.car.service.overlay.package";
+    field public static final String PROPERTY_CAR_SERVICE_OVERLAY_PACKAGES = "ro.android.car.service.overlay.packages";
   }
 
 }
diff --git a/car-builtin-lib/src/android/car/builtin/content/pm/PackageManagerHelper.java b/car-builtin-lib/src/android/car/builtin/content/pm/PackageManagerHelper.java
index 5f07e7b..0c3222da 100644
--- a/car-builtin-lib/src/android/car/builtin/content/pm/PackageManagerHelper.java
+++ b/car-builtin-lib/src/android/car/builtin/content/pm/PackageManagerHelper.java
@@ -39,8 +39,17 @@
 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public final class PackageManagerHelper {
 
-    public static final String PROPERTY_CAR_SERVICE_OVERLAY_PACKAGE_NAME =
-            "ro.android.car.service.overlay.package";
+    /**
+     * Read only property which contains semicolon (;) separated list of RRO packages.
+     *
+     * <p>
+     * RRO packages would be enabled if they are overlaying {@code CarServiceUpdatable}.
+     * {@code CarServiceUpdatable} can have different package names and this property may include
+     * all RROs to cover different {@code CarServiceUpdatable} package names but only those
+     * overriding the current {@code CarServiceUpdatable} package name will be selected.
+     */
+    public static final String PROPERTY_CAR_SERVICE_OVERLAY_PACKAGES =
+            "ro.android.car.service.overlay.packages";
 
     private PackageManagerHelper() {
         throw new UnsupportedOperationException("provides only static methods");
diff --git a/car_product/build/car.mk b/car_product/build/car.mk
index e672d06..61e3cdf 100644
--- a/car_product/build/car.mk
+++ b/car_product/build/car.mk
@@ -119,6 +119,11 @@
 PRODUCT_SYSTEM_PROPERTIES += \
     persist.wm.enable_remote_keyguard_animation=0
 
+# TODO(b/198516172): Find a better location to add this read only property
+# It is added here to check the functionality, will be updated in next CL
+PRODUCT_SYSTEM_PROPERTIES += \
+    ro.android.car.service.overlay.packages=com.android.car.resources.vendor;com.google.android.car.resources.vendor;
+
 # Automotive specific packages
 PRODUCT_PACKAGES += \
     CarFrameworkPackageStubs \
diff --git a/car_product/build/preinstalled-packages-product-car-base.xml b/car_product/build/preinstalled-packages-product-car-base.xml
index a04f61c..c9cfd77 100644
--- a/car_product/build/preinstalled-packages-product-car-base.xml
+++ b/car_product/build/preinstalled-packages-product-car-base.xml
@@ -243,6 +243,12 @@
         <install-in user-type="SYSTEM" />
     </install-in-user-type>
 
+    <!-- CarService updatable resources -->
+    <install-in-user-type package="com.android.car.resources.vendor">
+        <install-in user-type="FULL" />
+        <install-in user-type="SYSTEM" />
+    </install-in-user-type>
+
 <!--
   Apps that do need to run on SYSTEM and evaluated by package owner.
   Here the apps will have FULL only.
diff --git a/service-builtin/src/com/android/car/UpdatablePackageContext.java b/service-builtin/src/com/android/car/UpdatablePackageContext.java
index 3d247f7..883024c 100644
--- a/service-builtin/src/com/android/car/UpdatablePackageContext.java
+++ b/service-builtin/src/com/android/car/UpdatablePackageContext.java
@@ -16,11 +16,13 @@
 
 package com.android.car;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.car.builtin.content.pm.PackageManagerHelper;
 import android.car.builtin.util.Slogf;
 import android.content.Context;
 import android.content.ContextWrapper;
+import android.content.om.OverlayInfo;
 import android.content.om.OverlayManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -30,6 +32,11 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
 /** Context for updatable package */
 public class UpdatablePackageContext extends ContextWrapper {
 
@@ -85,49 +92,124 @@
         return info;
     }
 
+    // TODO(b/198516172): Add detailed description for the priority of RROs, who will replace whom.
     private static void enableRROForCarServiceUpdatable(Context baseContext) {
-        String packageName = SystemProperties.get(
-                PackageManagerHelper.PROPERTY_CAR_SERVICE_OVERLAY_PACKAGE_NAME,
-                /* default= */ null);
-        if (TextUtils.isEmpty(packageName)) {
-            // read only property not defined. No need to dynamically overlay resources.
-            Slogf.i(TAG, " %s is not set. No need to dynamically overlay resources.",
-                    PackageManagerHelper.PROPERTY_CAR_SERVICE_OVERLAY_PACKAGE_NAME);
+        List<String> packages = getEligibleRROPackages(baseContext);
+        if (packages.isEmpty()) {
+            Slogf.d(TAG, "No eligible RRO package to enable.");
             return;
         }
 
-        // check package
-        try {
-            PackageInfo info = baseContext.getPackageManager().getPackageInfo(packageName, 0);
-            if (info == null || info.applicationInfo == null
-                    || !(PackageManagerHelper.isSystemApp(info.applicationInfo)
-                    || PackageManagerHelper.isUpdatedSystemApp(info.applicationInfo)
-                    || PackageManagerHelper.isOemApp(info.applicationInfo)
-                    || PackageManagerHelper.isOdmApp(info.applicationInfo)
-                    || PackageManagerHelper.isVendorApp(info.applicationInfo)
-                    || PackageManagerHelper.isProductApp(info.applicationInfo)
-                    || PackageManagerHelper.isSystemExtApp(info.applicationInfo))) {
-                Slogf.i(TAG, "%s is not usable: %s", packageName, ((info == null)
-                        ? "package do not exist" : info.applicationInfo));
-                return;
-            }
-        } catch (Exception e) {
-            Slogf.w(TAG, e, "couldn't find package: %s", packageName);
-            return;
-        }
-
-        // package is valid. Enable RRO. This class is called for each user, so need to enable RRO
-        // for system and current user separately.
+        OverlayManager manager = baseContext.getSystemService(OverlayManager.class);
         UserHandle user = baseContext.getUser();
-        try {
-            OverlayManager manager = baseContext.getSystemService(OverlayManager.class);
-            manager.setEnabled(packageName, true, user);
-            Slogf.i(TAG, "RRO package %s is enabled for User %s", packageName, user);
-        } catch (Exception e) {
-            Slogf.w(TAG, e, "RRO package %s is NOT enabled for User %s", packageName, user);
+        for (int i = 0; i < packages.size(); i++) {
+            // This class is called for each user, so need to enable RRO for system and current user
+            // separately.
+            String rroPackageName = packages.get(i);
+            try {
+                manager.setEnabled(rroPackageName, /* enable= */true, user);
+                Slogf.d(TAG, "RRO package %s is enabled for User %s", rroPackageName, user);
+            } catch (Exception e) {
+                Slogf.w(TAG, e, "RRO package %s is NOT enabled for User %s", rroPackageName, user);
+            }
         }
     }
 
+    @NonNull
+    private static List<String> getEligibleRROPackages(Context baseContext) {
+        List<String> eligiblePackages = new ArrayList<>();
+
+        String packageNames = SystemProperties.get(
+                PackageManagerHelper.PROPERTY_CAR_SERVICE_OVERLAY_PACKAGES,
+                /* default= */ null);
+        if (TextUtils.isEmpty(packageNames)) {
+            // read only property not defined. No need to dynamically overlay resources.
+            Slogf.d(TAG, " %s is not set. No need to dynamically overlay resources.",
+                    PackageManagerHelper.PROPERTY_CAR_SERVICE_OVERLAY_PACKAGES);
+            return eligiblePackages;
+        }
+
+        Set<String> installedRROPackages = getInstalledRROPackages(baseContext);
+
+        if (installedRROPackages.isEmpty()) {
+            return eligiblePackages;
+        }
+
+        String[] packages = packageNames.split(";");
+        String rroPackageName;
+        for (int i = 0; i < packages.length; i++) {
+            rroPackageName = packages[i].trim();
+
+            if (rroPackageName.isEmpty()) {
+                continue;
+            }
+
+            if (!installedRROPackages.contains(rroPackageName)) {
+                Slogf.d(TAG, "RRO package %s is not installed.", rroPackageName);
+                continue;
+            }
+
+            // Check that package is part of the original image. A third party RRO
+            // should not be enabled using this.
+            try {
+                PackageInfo info = baseContext.getPackageManager().getPackageInfo(
+                        rroPackageName, 0);
+                // TODO(b/198516172): Move following logic to separate class and test it.
+                if (info == null || info.applicationInfo == null
+                        || !(PackageManagerHelper.isSystemApp(info.applicationInfo)
+                                || PackageManagerHelper.isUpdatedSystemApp(info.applicationInfo)
+                                || PackageManagerHelper.isOemApp(info.applicationInfo)
+                                || PackageManagerHelper.isOdmApp(info.applicationInfo)
+                                || PackageManagerHelper.isVendorApp(info.applicationInfo)
+                                || PackageManagerHelper.isProductApp(info.applicationInfo)
+                                || PackageManagerHelper.isSystemExtApp(info.applicationInfo))) {
+                    Slogf.d(TAG, "%s is not usable: %s", rroPackageName, ((info == null)
+                            ? "package do not exist"
+                            : info.applicationInfo));
+                    continue;
+                }
+            } catch (Exception e) {
+                Slogf.w(TAG, e, "couldn't find package: %s", rroPackageName);
+                continue;
+            }
+
+            // Add RRO package to the list.
+            Slogf.d(TAG, "RRO package %s is eligible for enabling.", rroPackageName);
+            eligiblePackages.add(rroPackageName);
+        }
+        return eligiblePackages;
+    }
+
+    @NonNull
+    private static Set<String> getInstalledRROPackages(Context baseContext) {
+        Set<String> installedOverlayPackages = new HashSet<>();
+        PackageInfo packageInfo = findUpdatableServicePackage(baseContext);
+        if (packageInfo == null) {
+            return installedOverlayPackages;
+        }
+        String updatablePackageName = packageInfo.packageName;
+
+        OverlayManager manager = baseContext.getSystemService(OverlayManager.class);
+        UserHandle user = baseContext.getUser();
+
+        List<OverlayInfo> installedOverlays = manager.getOverlayInfosForTarget(updatablePackageName,
+                user);
+
+        if (installedOverlays == null || installedOverlays.isEmpty()) {
+            return installedOverlayPackages;
+        }
+
+        for (int i = 0; i < installedOverlays.size(); i++) {
+            OverlayInfo overlayInfo = installedOverlays.get(i);
+            installedOverlayPackages.add(overlayInfo.getPackageName());
+        }
+
+        Slogf.d(TAG, "Total RROs packages for target package %s are %d.", updatablePackageName,
+                installedOverlayPackages.size());
+
+        return installedOverlayPackages;
+    }
+
     private UpdatablePackageContext(Context baseContext, Context packageContext) {
         super(baseContext);
         mPackageContext = packageContext;
diff --git a/service/res/values/overlayable.xml b/service/res/values/overlayable.xml
new file mode 100644
index 0000000..5c11eab
--- /dev/null
+++ b/service/res/values/overlayable.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+
+<!-- These values can be used to control CarService stack behavior/features on individual devices.
+     These can be overridden by OEM's by using an RRO overlay app.
+     See <update APP location> for a sample overlay app. -->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <overlayable name="CarServiceCustomization">
+        <!-- START VENDOR CUSTOMIZATION -->
+        <policy type="product|system|vendor">
+          <!-- Params from config.xml that can be overlaid -->
+          <item type="bool" name="audioUseDynamicRouting"/>
+          <item type="bool" name="audioUseCarVolumeGroupMuting"/>
+          <item type="bool" name="audioUseHalDuckingSignals"/>
+          <item type="integer" name="audioVolumeAdjustmentContextsVersion"/>
+          <item type="bool" name="audioPersistMasterMuteState"/>
+          <item type="integer" name="audioVolumeKeyEventTimeoutMs"/>
+          <item type="bool" name="displayOffMuteLockAllAudio"/>
+          <item type="bool" name="useDefaultBluetoothConnectionPolicy"/>
+          <item type="string" name="instrumentClusterRendererService" translatable="false"/>
+          <item type="string" name="config_clusterHomeActivity" translatable="false"/>
+          <item type="string" name="rotaryService" translatable="false"/>
+          <item type="bool" name="enableActivityBlockingForSafety"/>
+          <item type="string" name="activityBlockingActivity" translatable="false"/>
+          <item type="string" name="continuousBlankActivity" translatable="false"/>
+          <item type="string" name="activityAllowlist" translatable="false"/>
+          <item type="string" name="systemActivityAllowlist" translatable="false"/>
+          <item type="string" name="activityDenylist" translatable="false"/>
+          <item type="array" name="allowedAppInstallSources" translatable="false"/>
+          <item type="string" name="defaultHomeActivity" translatable="false"/>
+          <item type="integer" name="vmsHalClientMetricsProperty"/>
+          <item type="array" name="vmsPublisherSystemClients" translatable="false"/>
+          <item type="array" name="vmsPublisherUserClients" translatable="false"/>
+          <item type="integer" name="millisecondsBeforeRebindToVmsPublisher"/>
+          <item type="integer" name="acceptableHoursPerOnePercentFlashWear"/>
+          <item type="integer" name="uptimeHoursIntervalBetweenUptimeDataWrite"/>
+          <item type="string" name="activityHandlerForFlashWearChanges" translatable="false"/>
+          <item type="integer" name="ioStatsRefreshRateSeconds"/>
+          <item type="integer" name="ioStatsNumSamplesToStore"/>
+          <item type="integer" name="acceptableWrittenKBytesPerSample"/>
+          <item type="integer" name="acceptableFsyncCallsPerSample"/>
+          <item type="integer" name="maxExcessiveIoSamplesInWindow"/>
+          <item type="integer" name="recurringResourceOverusePeriodInDays"/>
+          <item type="integer" name="recurringResourceOveruseTimes"/>
+          <item type="integer" name="uidIoUsageSummaryTopCount"/>
+          <item type="integer" name="ioUsageSummaryMinSystemTotalWrittenBytes"/>
+          <item type="string" name="intentReceiverForUnacceptableIoMetrics" translatable="false"/>
+          <item type="string" name="eMmcLifetimeFilePath" translatable="false"/>
+          <item type="string" name="eMmcEolFilePath" translatable="false"/>
+          <item type="integer" name="fastPairModelId"/>
+          <item type="string" name="fastPairAntiSpoofKey" translatable="false"/>
+          <item type="bool" name="fastPairAutomaticAcceptance"/>
+          <item type="integer" name="maxGarageModeRunningDurationInSecs"/>
+          <item type="array" name="config_earlyStartupServices" translatable="false"/>
+          <item type="string" name="config_projectionConsentActivity" translatable="false"/>
+          <item type="integer" name="config_projectionActivityDisplayId"/>
+          <item type="array" name="config_projectionActivityLaunchBounds"/>
+          <item type="integer" name="config_projectionUiMode"/>
+          <item type="bool" name="config_projectionAccessPointTethering"/>
+          <item type="bool" name="config_stableLocalOnlyHotspotConfig"/>
+          <item type="string" name="serviceMediaConnection" translatable="false"/>
+          <item type="string" name="config_car_bugreport_application" translatable="false"/>
+          <item type="array" name="config_occupant_zones" translatable="false"/>
+          <item type="array" name="config_occupant_display_mapping" translatable="false"/>
+          <item type="array" name="config_sourcePreferredComponents" translatable="false"/>
+          <item type="string" name="config_userNoticeUiService" translatable="false"/>
+          <item type="integer" name="config_mediaSourceChangedAutoplay"/>
+          <item type="integer" name="config_mediaBootAutoplay"/>
+          <item type="bool" name="config_mediaSourceIndependentPlayback"/>
+          <item type="array" name="config_allowed_optional_car_features" translatable="false"/>
+          <item type="bool" name="enablePassengerSupport"/>
+          <item type="string" name="config_customCountryDetector" translatable="false"/>
+          <item type="bool" name="enableLongPressBluetoothVoiceRecognition"/>
+          <item type="bool" name="config_switchGuestUserBeforeGoingSleep"/>
+          <item type="bool" name="enableProfileUserAssignmentForMultiDisplay"/>
+          <item type="string" name="config_defaultMediaSource" translatable="false"/>
+          <item type="bool" name="config_callButtonEndsOngoingCall"/>
+          <item type="integer" name="config_maxSuspendWaitDuration"/>
+          <item type="integer" name="config_preShutdownPrepareTimeout"/>
+          <item type="integer" name="config_shutdownEnterTimeout"/>
+          <item type="integer" name="config_postShutdownEnterTimeout"/>
+          <item type="string" name="config_evsRearviewCameraId" translatable="false"/>
+          <item type="string" name="config_evsCameraActivity" translatable="false"/>
+          <item type="bool" name="config_wifiAdjustmentForSuspend"/>
+          <item type="bool" name="config_enableCarLocationServiceGnssControlsForPowerManagement"/>
+          <item type="bool" name="config_preventTemplatedAppsFromShowingDialog"/>
+          <item type="string" name="config_template_activity_class_name" translatable="false"/>
+          <item type="bool" name="config_enableExternalCarTimeToExternalTimeSuggestion"/>
+          <!-- Params from config.xml that can be overlaid -->
+        </policy>
+        <!-- END VENDOR CUSTOMIZATION -->
+    </overlayable>
+</resources>
\ No newline at end of file
diff --git a/service/src/com/android/car/pm/CarPackageManagerService.java b/service/src/com/android/car/pm/CarPackageManagerService.java
index bd8948b..e1df7d9 100644
--- a/service/src/com/android/car/pm/CarPackageManagerService.java
+++ b/service/src/com/android/car/pm/CarPackageManagerService.java
@@ -1212,9 +1212,11 @@
             writer.println(mCurrentDrivingSafetyRegion);
             writer.print("mTempAllowedActivities:");
             writer.println(mTempAllowedActivities);
-            writer.println("Car service overlay package name: "
+            writer.println("Car service overlay packages property name: "
+                    + PackageManagerHelper.PROPERTY_CAR_SERVICE_OVERLAY_PACKAGES);
+            writer.println("Car service overlay packages: "
                     + SystemProperties.get(
-                            PackageManagerHelper.PROPERTY_CAR_SERVICE_OVERLAY_PACKAGE_NAME,
+                            PackageManagerHelper.PROPERTY_CAR_SERVICE_OVERLAY_PACKAGES,
                             /* default= */ null));
         }
     }