Merge "Hide system apps until installed (1/2)" into pi-dev
am: 70a9f39e6a

Change-Id: Ide5ea4b18ca95ee061bf84cb349369b2468504d1
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index d65e051..3120421 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -1120,6 +1120,9 @@
     /** @hide */
     public String[] splitClassLoaderNames;
 
+    /** @hide */
+    public boolean hiddenUntilInstalled;
+
     /**
      * Represents the default policy. The actual policy used will depend on other properties of
      * the application, e.g. the target SDK version.
@@ -1460,6 +1463,7 @@
         compileSdkVersion = orig.compileSdkVersion;
         compileSdkVersionCodename = orig.compileSdkVersionCodename;
         mHiddenApiPolicy = orig.mHiddenApiPolicy;
+        hiddenUntilInstalled = orig.hiddenUntilInstalled;
     }
 
     public String toString() {
@@ -1534,6 +1538,7 @@
         dest.writeString(compileSdkVersionCodename);
         dest.writeString(appComponentFactory);
         dest.writeInt(mHiddenApiPolicy);
+        dest.writeInt(hiddenUntilInstalled ? 1 : 0);
     }
 
     public static final Parcelable.Creator<ApplicationInfo> CREATOR
@@ -1605,6 +1610,7 @@
         compileSdkVersionCodename = source.readString();
         appComponentFactory = source.readString();
         mHiddenApiPolicy = source.readInt();
+        hiddenUntilInstalled = source.readInt() != 0;
     }
 
     /**
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index c988fa9..bc5b32c 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -598,6 +598,9 @@
     boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden, int userId);
     boolean getApplicationHiddenSettingAsUser(String packageName, int userId);
 
+    void setSystemAppHiddenUntilInstalled(String packageName, boolean hidden);
+    boolean setSystemAppInstallState(String packageName, boolean installed, int userId);
+
     IPackageInstaller getPackageInstaller();
 
     boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, int userId);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 7253e77..68d0da9 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -147,6 +147,7 @@
             GET_DISABLED_COMPONENTS,
             GET_DISABLED_UNTIL_USED_COMPONENTS,
             GET_UNINSTALLED_PACKAGES,
+            MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface PackageInfoFlags {}
@@ -164,6 +165,7 @@
             MATCH_STATIC_SHARED_LIBRARIES,
             GET_DISABLED_UNTIL_USED_COMPONENTS,
             GET_UNINSTALLED_PACKAGES,
+            MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ApplicationInfoFlags {}
@@ -522,6 +524,12 @@
     public static final int MATCH_DEBUG_TRIAGED_MISSING = 0x10000000;
 
     /**
+     * Internal flag used to indicate that a package is a hidden system app.
+     * @hide
+     */
+    public static final int MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS =  0x20000000;
+
+    /**
      * Flag for {@link #addCrossProfileIntentFilter}: if this flag is set: when
      * resolving an intent that matches the {@code CrossProfileIntentFilter},
      * the current profile will be skipped. Only activities in the target user
@@ -4841,7 +4849,8 @@
      * on the system for other users, also install it for the specified user.
      * @hide
      */
-     @RequiresPermission(anyOf = {
+    @RequiresPermission(anyOf = {
+            Manifest.permission.INSTALL_EXISTING_PACKAGES,
             Manifest.permission.INSTALL_PACKAGES,
             Manifest.permission.INTERACT_ACROSS_USERS_FULL})
     public abstract int installExistingPackageAsUser(String packageName, @UserIdInt int userId)
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 3f8a390..79f3e53 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -639,11 +639,19 @@
      */
     private static boolean checkUseInstalledOrHidden(int flags, PackageUserState state,
             ApplicationInfo appInfo) {
+        // Returns false if the package is hidden system app until installed.
+        if ((flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) == 0
+                && !state.installed
+                && appInfo != null && appInfo.hiddenUntilInstalled) {
+            return false;
+        }
+
         // If available for the target user, or trying to match uninstalled packages and it's
         // a system app.
         return state.isAvailable(flags)
                 || (appInfo != null && appInfo.isSystemApp()
-                        && (flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0);
+                        && ((flags & PackageManager.MATCH_KNOWN_PACKAGES) != 0
+                        || (flags & PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS) != 0));
     }
 
     public static boolean isAvailable(PackageUserState state) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4e74d50..cda1293 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3025,6 +3025,15 @@
     <permission android:name="android.permission.INSTALL_PACKAGE_UPDATES"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Allows an application to install existing system packages. This is a limited
+         version of {@link android.Manifest.permission#INSTALL_PACKAGES}.
+         <p>Not for use by third-party applications.
+         TODO(b/80204953): remove this permission once we have a long-term solution.
+         @hide
+    -->
+    <permission android:name="com.android.permission.INSTALL_EXISTING_PACKAGES"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @SystemApi Allows an application to clear user data.
          <p>Not for use by third-party applications
          @hide
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 9ed2b9c..c2d6798 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -13967,6 +13967,68 @@
         return false;
     }
 
+    @Override
+    public void setSystemAppHiddenUntilInstalled(String packageName, boolean hidden) {
+        enforceSystemOrPhoneCaller("setSystemAppHiddenUntilInstalled");
+        synchronized (mPackages) {
+            final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
+            if (pkgSetting == null || !pkgSetting.isSystem()) {
+                return;
+            }
+            PackageParser.Package pkg = pkgSetting.pkg;
+            if (pkg != null && pkg.applicationInfo != null) {
+                pkg.applicationInfo.hiddenUntilInstalled = hidden;
+            }
+            final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(packageName);
+            if (disabledPs == null) {
+                return;
+            }
+            pkg = disabledPs.pkg;
+            if (pkg != null && pkg.applicationInfo != null) {
+                pkg.applicationInfo.hiddenUntilInstalled = hidden;
+            }
+        }
+    }
+
+    @Override
+    public boolean setSystemAppInstallState(String packageName, boolean installed, int userId) {
+        enforceSystemOrPhoneCaller("setSystemAppInstallState");
+        synchronized (mPackages) {
+            final PackageSetting pkgSetting = mSettings.mPackages.get(packageName);
+            // The target app should always be in system
+            if (pkgSetting == null || !pkgSetting.isSystem()) {
+                return false;
+            }
+            // Check if the install state is the same
+            if (pkgSetting.getInstalled(userId) == installed) {
+                return false;
+            }
+        }
+
+        final long callingId = Binder.clearCallingIdentity();
+        try {
+            if (installed) {
+                // install the app from uninstalled state
+                installExistingPackageAsUser(
+                        packageName,
+                        userId,
+                        0 /*installFlags*/,
+                        PackageManager.INSTALL_REASON_DEVICE_SETUP);
+                return true;
+            }
+
+            // uninstall the app from installed state
+            deletePackageVersioned(
+                    new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
+                    new LegacyPackageDeleteObserver(null).getBinder(),
+                    userId,
+                    PackageManager.DELETE_SYSTEM_APP);
+            return true;
+        } finally {
+            Binder.restoreCallingIdentity(callingId);
+        }
+    }
+
     private void sendApplicationHiddenForUser(String packageName, PackageSetting pkgSetting,
             int userId) {
         final PackageRemovedInfo info = new PackageRemovedInfo(this);
@@ -14031,10 +14093,16 @@
     @Override
     public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
             int installReason) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
-                null);
-        PackageSetting pkgSetting;
         final int callingUid = Binder.getCallingUid();
+        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES)
+                != PackageManager.PERMISSION_GRANTED
+                && mContext.checkCallingOrSelfPermission(
+                        android.Manifest.permission.INSTALL_EXISTING_PACKAGES)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Neither user " + callingUid + " nor current process has "
+                    + android.Manifest.permission.INSTALL_PACKAGES + ".");
+        }
+        PackageSetting pkgSetting;
         mPermissionManager.enforceCrossUserPermission(callingUid, userId,
                 true /* requireFullPermission */, true /* checkShell */,
                 "installExistingPackage for user " + userId);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 5177995..a7e3830 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4120,7 +4120,8 @@
                     continue;
                 }
                 final boolean shouldInstall = ps.isSystem() &&
-                        !ArrayUtils.contains(disallowedPackages, ps.name);
+                        !ArrayUtils.contains(disallowedPackages, ps.name) &&
+                        !ps.pkg.applicationInfo.hiddenUntilInstalled;
                 // Only system apps are initially installed.
                 ps.setInstalled(shouldInstall, userHandle);
                 if (!shouldInstall) {
diff --git a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
index bcad554..a1bea4d 100644
--- a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
+++ b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java
@@ -21,7 +21,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.content.res.Resources;
 import android.os.RemoteException;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
@@ -145,6 +144,18 @@
                         telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
                                 TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
 
+                // add hiddenUntilInstalled flag for carrier apps and associated apps
+                packageManager.setSystemAppHiddenUntilInstalled(packageName, true);
+                List<ApplicationInfo> associatedAppList = associatedApps.get(packageName);
+                if (associatedAppList != null) {
+                    for (ApplicationInfo associatedApp : associatedAppList) {
+                        packageManager.setSystemAppHiddenUntilInstalled(
+                                associatedApp.packageName,
+                                true
+                        );
+                    }
+                }
+
                 if (hasPrivileges) {
                     // Only update enabled state for the app on /system. Once it has been
                     // updated we shouldn't touch it.
@@ -152,9 +163,14 @@
                             && (ai.enabledSetting ==
                             PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                             || ai.enabledSetting ==
-                            PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
+                            PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
+                            || (ai.flags & ApplicationInfo.FLAG_INSTALLED) == 0)) {
                         Slog.i(TAG, "Update state(" + packageName + "): ENABLED for user "
                                 + userId);
+                        packageManager.setSystemAppInstallState(
+                                packageName,
+                                true /*installed*/,
+                                userId);
                         packageManager.setApplicationEnabledSetting(
                                 packageName,
                                 PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
@@ -164,15 +180,20 @@
                     }
 
                     // Also enable any associated apps for this carrier app.
-                    List<ApplicationInfo> associatedAppList = associatedApps.get(packageName);
                     if (associatedAppList != null) {
                         for (ApplicationInfo associatedApp : associatedAppList) {
                             if (associatedApp.enabledSetting ==
                                     PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                                     || associatedApp.enabledSetting ==
-                                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
+                                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
+                                    || (associatedApp.flags
+                                    & ApplicationInfo.FLAG_INSTALLED) == 0) {
                                 Slog.i(TAG, "Update associated state(" + associatedApp.packageName
                                         + "): ENABLED for user " + userId);
+                                packageManager.setSystemAppInstallState(
+                                        associatedApp.packageName,
+                                        true /*installed*/,
+                                        userId);
                                 packageManager.setApplicationEnabledSetting(
                                         associatedApp.packageName,
                                         PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
@@ -190,36 +211,33 @@
                     // updated we shouldn't touch it.
                     if (!ai.isUpdatedSystemApp()
                             && ai.enabledSetting ==
-                            PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+                            PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+                            && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
                         Slog.i(TAG, "Update state(" + packageName
                                 + "): DISABLED_UNTIL_USED for user " + userId);
-                        packageManager.setApplicationEnabledSetting(
+                        packageManager.setSystemAppInstallState(
                                 packageName,
-                                PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
-                                0,
-                                userId,
-                                callingPackage);
+                                false /*installed*/,
+                                userId);
                     }
 
                     // Also disable any associated apps for this carrier app if this is the first
                     // run. We avoid doing this a second time because it is brittle to rely on the
                     // distinction between "default" and "enabled".
                     if (!hasRunOnce) {
-                        List<ApplicationInfo> associatedAppList = associatedApps.get(packageName);
                         if (associatedAppList != null) {
                             for (ApplicationInfo associatedApp : associatedAppList) {
                                 if (associatedApp.enabledSetting
-                                        == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) {
+                                        == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+                                        && (associatedApp.flags
+                                        & ApplicationInfo.FLAG_INSTALLED) != 0) {
                                     Slog.i(TAG,
                                             "Update associated state(" + associatedApp.packageName
                                                     + "): DISABLED_UNTIL_USED for user " + userId);
-                                    packageManager.setApplicationEnabledSetting(
+                                    packageManager.setSystemAppInstallState(
                                             associatedApp.packageName,
-                                            PackageManager
-                                                    .COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED,
-                                            0,
-                                            userId,
-                                            callingPackage);
+                                            false /*installed*/,
+                                            userId);
                                 }
                             }
                         }
@@ -357,7 +375,8 @@
             String packageName) {
         try {
             ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
-                    PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, userId);
+                    PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                    | PackageManager.MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS, userId);
             if (ai != null && ai.isSystemApp()) {
                 return ai;
             }