Allowlist for exempting packages from being marked as stopped by default

An allowlist for packages that should not be scanned in a "stopped" state.

Bug: 249514169
Test: Manual. Added a system package in the allowlist, performed factory
reset and checked the stopped state of the same package after 1st boot.

Change-Id: Iec603243d7c95d4cdf207f7873373a851698b243
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index d0c3e5f..f233c6e 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -36,6 +36,12 @@
 }
 
 prebuilt_etc {
+    name: "initial-package-stopped-states.xml",
+    sub_dir: "sysconfig",
+    src: "initial-package-stopped-states.xml",
+}
+
+prebuilt_etc {
     name: "preinstalled-packages-platform-overlays.xml",
     product_specific: true,
     sub_dir: "sysconfig",
diff --git a/data/etc/initial-package-stopped-states.xml b/data/etc/initial-package-stopped-states.xml
new file mode 100644
index 0000000..6bda2c0
--- /dev/null
+++ b/data/etc/initial-package-stopped-states.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 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 an allowlist for packages that should not be scanned in a "stopped" state.
+When this feature is turned on (indicated by the config config_stopSystemPackagesByDefault in
+core/res/res/values/config.xml) packages on the system partition that are encountered by
+the PackageManagerService for the first time are scanned in the "stopped" state. This allowlist
+is also considered while creating new users on the device. Stopped state is not set during
+subsequent reboots.
+
+Example usage
+    1. <initial-package-state package="com.example.app" stopped="false"/>
+        Indicates that a system package - com.example.app's initial stopped state should not be set
+        by the Package Manager. By default, system apps are marked as stopped.
+    2. <initial-package-state package="com.example.app" stopped="true"/>
+        Indicates that a system package - com.example.app's initial state should be set by the
+        Package Manager to "stopped=true". It will have the same effect on the
+        package's stopped state even if this package was not included in the allow list.
+    3. <initial-package-state package="com.example.app"/>
+        Invalid usage.
+-->
+
+<config></config>
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 4854c37..4b76127 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -333,6 +333,11 @@
     // Update ownership for system applications and the installers eligible to update them.
     private final ArrayMap<String, String> mUpdateOwnersForSystemApps = new ArrayMap<>();
 
+    // Set of package names that should not be marked as "stopped" during initial device boot
+    // or when adding a new user. A new package not contained in this set will be
+    // marked as stopped by the system
+    @NonNull private final Set<String> mInitialNonStoppedSystemPackages = new ArraySet<>();
+
     /**
      * Map of system pre-defined, uniquely named actors; keys are namespace,
      * value maps actor name to package name.
@@ -527,6 +532,10 @@
                 ? null : mOverlayConfigSignaturePackage;
     }
 
+    public Set<String> getInitialNonStoppedSystemPackages() {
+        return mInitialNonStoppedSystemPackages;
+    }
+
     /**
      * Only use for testing. Do NOT use in production code.
      * @param readPermissions false to create an empty SystemConfig; true to read the permissions.
@@ -1445,6 +1454,19 @@
                         }
                         XmlUtils.skipCurrentTag(parser);
                     } break;
+                    case "initial-package-state": {
+                        String pkgName = parser.getAttributeValue(null, "package");
+                        String stopped = parser.getAttributeValue(null, "stopped");
+                        if (TextUtils.isEmpty(pkgName)) {
+                            Slog.w(TAG, "<" + name + "> without package in " + permFile
+                                    + " at " + parser.getPositionDescription());
+                        } else if (TextUtils.isEmpty(stopped)) {
+                            Slog.w(TAG, "<" + name + "> without stopped in " + permFile
+                                    + " at " + parser.getPositionDescription());
+                        } else if (!Boolean.parseBoolean(stopped)) {
+                            mInitialNonStoppedSystemPackages.add(pkgName);
+                        }
+                    }
                     default: {
                         Slog.w(TAG, "Tag " + name + " is unknown in "
                                 + permFile + " at " + parser.getPositionDescription());