diff options
8 files changed, 148 insertions, 25 deletions
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index d65e051b70b4..3120421622a4 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -1120,6 +1120,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { /** @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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { compileSdkVersion = orig.compileSdkVersion; compileSdkVersionCodename = orig.compileSdkVersionCodename; mHiddenApiPolicy = orig.mHiddenApiPolicy; + hiddenUntilInstalled = orig.hiddenUntilInstalled; } public String toString() { @@ -1534,6 +1538,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { 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 c988fa907f86..bc5b32c69b59 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -598,6 +598,9 @@ interface IPackageManager { 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 7253e7758008..68d0da970b63 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -147,6 +147,7 @@ public abstract class PackageManager { 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 @@ public abstract class PackageManager { 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 abstract class PackageManager { 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 @@ public abstract class PackageManager { * 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 3f8a390c215e..79f3e53f1c9e 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -639,11 +639,19 @@ public class PackageParser { */ 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 4e74d5066b6e..cda1293e93c7 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 9ed2b9c18546..c2d679858925 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 @@ public class PackageManagerService extends IPackageManager.Stub 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 @@ public class PackageManagerService extends IPackageManager.Stub @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 5177995a8353..a7e38301bb40 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 @@ public final class Settings { 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 bcad554b579f..a1bea4d417f9 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.ContentResolver; 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 @@ public final class CarrierAppUtils { 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 @@ public final class CarrierAppUtils { && (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 @@ public final class CarrierAppUtils { } // 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 @@ public final class CarrierAppUtils { // 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 @@ public final class CarrierAppUtils { 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; } |