Merge "Introduced PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER"
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 807c0a2..fe279d1 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -506,6 +506,14 @@
     public static final int PRIVATE_FLAG_EPHEMERAL = 1 << 9;
 
     /**
+     * When set, signals that the application is required for the system user and should not be
+     * uninstalled.
+     *
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER = 1 << 10;
+
+    /**
      * Private/hidden flags. See {@code PRIVATE_FLAG_...} constants.
      * {@hide}
      */
@@ -1117,6 +1125,13 @@
     /**
      * @hide
      */
+    public boolean isRequiredForSystemUser() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER) != 0;
+    }
+
+    /**
+     * @hide
+     */
     @Override protected ApplicationInfo getApplicationInfo() {
         return this;
     }
diff --git a/core/java/android/content/pm/AppsQueryHelper.java b/core/java/android/content/pm/AppsQueryHelper.java
index 084bc18..e542589 100644
--- a/core/java/android/content/pm/AppsQueryHelper.java
+++ b/core/java/android/content/pm/AppsQueryHelper.java
@@ -51,6 +51,11 @@
      */
     public static int GET_IMES = 1 << 2;
 
+    /**
+     * Return all apps that are flagged as required for the system user.
+     */
+    public static int GET_REQUIRED_FOR_SYSTEM_USER = 1 << 3;
+
     private final IPackageManager mPackageManager;
     private List<ApplicationInfo> mAllApps;
 
@@ -73,6 +78,7 @@
         boolean nonLaunchableApps = (flags & GET_NON_LAUNCHABLE_APPS) > 0;
         boolean interactAcrossUsers = (flags & GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM) > 0;
         boolean imes = (flags & GET_IMES) > 0;
+        boolean requiredForSystemUser = (flags & GET_REQUIRED_FOR_SYSTEM_USER) > 0;
         if (mAllApps == null) {
             mAllApps = getAllApps(user.getIdentifier());
         }
@@ -143,6 +149,18 @@
             }
         }
 
+        if (requiredForSystemUser) {
+            final int allAppsSize = mAllApps.size();
+            for (int i = 0; i < allAppsSize; i++) {
+                final ApplicationInfo appInfo = mAllApps.get(i);
+                if (systemAppsOnly && !appInfo.isSystemApp()) {
+                    continue;
+                }
+                if (appInfo.isRequiredForSystemUser()) {
+                    result.add(appInfo.packageName);
+                }
+            }
+        }
         return result;
     }
 
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 4d0d146..39f59554 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -523,4 +523,6 @@
     boolean setEphemeralApplicationCookie(String packageName, in byte[] cookie, int userId);
     Bitmap getEphemeralApplicationIcon(String packageName, int userId);
     boolean isEphemeralApplication(String packageName, int userId);
+
+    boolean setRequiredForSystemUser(String packageName, boolean systemUserApp);
 }
diff --git a/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java b/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java
index dcf2c89..9c03e1a 100644
--- a/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java
+++ b/core/tests/coretests/src/android/content/pm/AppsQueryHelperTests.java
@@ -90,6 +90,18 @@
         assertEqualsIgnoreOrder(Arrays.asList("sys_app1", "app4"), apps);
     }
 
+    public void testQueryAppsRequiredForSystemUser() {
+        // Test query only system apps required for system user
+        List<String> apps = mAppsQueryHelper.queryApps(AppsQueryHelper.GET_REQUIRED_FOR_SYSTEM_USER,
+                true, UserHandle.SYSTEM);
+        assertEqualsIgnoreOrder(Arrays.asList("sys_app3"), apps);
+
+        // Test query all apps required for system user
+        apps = mAppsQueryHelper.queryApps(AppsQueryHelper.GET_REQUIRED_FOR_SYSTEM_USER, false,
+                UserHandle.SYSTEM);
+        assertEqualsIgnoreOrder(Arrays.asList("sys_app3", "app4"), apps);
+    }
+
     private class AppsQueryHelperTestable extends AppsQueryHelper {
 
         @Override
@@ -104,7 +116,9 @@
             final ApplicationInfo ai3 = new ApplicationInfo();
             ai3.packageName = "sys_app3";
             ai3.flags |= ApplicationInfo.FLAG_SYSTEM;
+            ai3.privateFlags |= ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER;
             final ApplicationInfo ai4 = new ApplicationInfo();
+            ai4.privateFlags |= ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER;
             ai4.packageName = "app4";
             return Arrays.asList(ai1, ai2, ai3, ai4);
         }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d64b898..42e8b01 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1845,12 +1845,12 @@
             boolean factoryTest, boolean onlyCore) {
         PackageManagerService m = new PackageManagerService(context, installer,
                 factoryTest, onlyCore);
-        m.enableSystemUserApps();
+        m.enableSystemUserPackages();
         ServiceManager.addService("package", m);
         return m;
     }
 
-    private void enableSystemUserApps() {
+    private void enableSystemUserPackages() {
         if (!UserManager.isSplitSystemUser()) {
             return;
         }
@@ -1867,48 +1867,32 @@
                 | AppsQueryHelper.GET_IMES, /* systemAppsOnly */ true, UserHandle.SYSTEM));
         ArraySet<String> wlApps = SystemConfig.getInstance().getSystemUserWhitelistedApps();
         enableApps.addAll(wlApps);
+        enableApps.addAll(queryHelper.queryApps(AppsQueryHelper.GET_REQUIRED_FOR_SYSTEM_USER,
+                /* systemAppsOnly */ false, UserHandle.SYSTEM));
         ArraySet<String> blApps = SystemConfig.getInstance().getSystemUserBlacklistedApps();
         enableApps.removeAll(blApps);
-
-        List<String> systemApps = queryHelper.queryApps(0, /* systemAppsOnly */ true,
+        Log.i(TAG, "Applications installed for system user: " + enableApps);
+        List<String> allAps = queryHelper.queryApps(0, /* systemAppsOnly */ false,
                 UserHandle.SYSTEM);
-        final int systemAppsSize = systemApps.size();
+        final int allAppsSize = allAps.size();
         synchronized (mPackages) {
-            for (int i = 0; i < systemAppsSize; i++) {
-                String pName = systemApps.get(i);
+            for (int i = 0; i < allAppsSize; i++) {
+                String pName = allAps.get(i);
                 PackageSetting pkgSetting = mSettings.mPackages.get(pName);
                 // Should not happen, but we shouldn't be failing if it does
                 if (pkgSetting == null) {
                     continue;
                 }
-                boolean installed = enableApps.contains(pName);
-                pkgSetting.setInstalled(installed, UserHandle.USER_SYSTEM);
+                boolean install = enableApps.contains(pName);
+                if (pkgSetting.getInstalled(UserHandle.USER_SYSTEM) != install) {
+                    Log.i(TAG, (install ? "Installing " : "Uninstalling ") + pName
+                            + " for system user");
+                    pkgSetting.setInstalled(install, UserHandle.USER_SYSTEM);
+                }
             }
         }
     }
 
-    static String[] splitString(String str, char sep) {
-        int count = 1;
-        int i = 0;
-        while ((i=str.indexOf(sep, i)) >= 0) {
-            count++;
-            i++;
-        }
-
-        String[] res = new String[count];
-        i=0;
-        count = 0;
-        int lastI=0;
-        while ((i=str.indexOf(sep, i)) >= 0) {
-            res[count] = str.substring(lastI, i);
-            count++;
-            i++;
-            lastI = i;
-        }
-        res[count] = str.substring(lastI, str.length());
-        return res;
-    }
-
     private static void getDefaultDisplayMetrics(Context context, DisplayMetrics metrics) {
         DisplayManager displayManager = (DisplayManager) context.getSystemService(
                 Context.DISPLAY_SERVICE);
@@ -13782,6 +13766,29 @@
         }
     }
 
+    @Override
+    public boolean setRequiredForSystemUser(String packageName, boolean systemUserApp) {
+        int callingUid = Binder.getCallingUid();
+        if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID) {
+            throw new SecurityException(
+                    "setRequiredForSystemUser can only be run by the system or root");
+        }
+        synchronized (mPackages) {
+            PackageSetting ps = mSettings.mPackages.get(packageName);
+            if (ps == null) {
+                Log.w(TAG, "Package doesn't exist: " + packageName);
+                return false;
+            }
+            if (systemUserApp) {
+                ps.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER;
+            } else {
+                ps.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER;
+            }
+            mSettings.writeLPr();
+        }
+        return true;
+    }
+
     /*
      * This method handles package deletion in general
      */
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 5cf92a9..2921032 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -49,6 +49,7 @@
     void setPrivateFlags(int pkgPrivateFlags) {
         this.pkgPrivateFlags = pkgPrivateFlags
                 & (ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
-                        | ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK);
+                | ApplicationInfo.PRIVATE_FLAG_FORWARD_LOCK
+                | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
     }
 }