From 24b9d960071ecf24f1b7edf799f6a4edf20f2b95 Mon Sep 17 00:00:00 2001 From: Jeff Davidson Date: Thu, 21 Jul 2016 12:35:10 -0700 Subject: Add support for carrier "associated" apps. The platform currently supports the notion of default carrier apps. These apps are set to DISABLED_UNTIL_USED until a SIM is inserted which grants them carrier privileges, at which point they are enabled. Apps are not touched if they have been updated from the version on /system or if their state has been modified externally (e.g. by the user). This CL extends this notion to associated apps, which may not have carrier privileges themselves, but should be enabled/disabled alongside a particular carrier app. This should include helper apps that should not be visible to users who don't use the given carrier unless the user explicitly enables the app. As additional protection, we add a check to ensure that we never disable apps after the first time we've run. Since we need to store this information in secure settings, we also move the call site from PackageManagerService#main() to PackageManagerService#systemReady(), which enables use of secure settings but still occurs before third-party apps can be started. Bug: 30141427 Change-Id: Iee72ba4e70e5ca97999c9147a65af82c670a23e8 --- core/java/android/provider/Settings.java | 14 ++ core/java/com/android/server/SystemConfig.java | 31 ++++ .../android/server/pm/PackageManagerService.java | 9 +- .../internal/telephony/CarrierAppUtils.java | 206 +++++++++++++++++---- 4 files changed, 216 insertions(+), 44 deletions(-) diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 72420434ec7f..77dac1434eef 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6213,6 +6213,20 @@ public final class Settings { */ public static final int VR_DISPLAY_MODE_OFF = 1; + /** + * Whether CarrierAppUtils#disableCarrierAppsUntilPrivileged has been executed at least + * once. + * + *

This is used to ensure that we only take one pass which will disable apps that are not + * privileged (if any). From then on, we only want to enable apps (when a matching SIM is + * inserted), to avoid disabling an app that the user might actively be using. + * + *

Will be set to 1 once executed. + * + * @hide + */ + public static final String CARRIER_APPS_HANDLED = "carrier_apps_handled"; + /** * Whether parent user can access remote contact in managed profile. * diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index 886265ac6df1..429131bfd320 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -42,6 +42,8 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; +import java.util.ArrayList; +import java.util.List; /** * Loads global system configuration info. @@ -122,6 +124,11 @@ public class SystemConfig { // These are the permitted backup transport service components final ArraySet mBackupTransportWhitelist = new ArraySet<>(); + // These are the packages of carrier-associated apps which should be disabled until used until + // a SIM is inserted which grants carrier privileges to that carrier app. + final ArrayMap> mDisabledUntilUsedPreinstalledCarrierAssociatedApps = + new ArrayMap<>(); + public static SystemConfig getInstance() { synchronized (SystemConfig.class) { if (sInstance == null) { @@ -183,6 +190,10 @@ public class SystemConfig { return mBackupTransportWhitelist; } + public ArrayMap> getDisabledUntilUsedPreinstalledCarrierAssociatedApps() { + return mDisabledUntilUsedPreinstalledCarrierAssociatedApps; + } + SystemConfig() { // Read configuration from system readPermissions(Environment.buildPath( @@ -476,6 +487,26 @@ public class SystemConfig { } } XmlUtils.skipCurrentTag(parser); + } else if ("disabled-until-used-preinstalled-carrier-associated-app".equals(name) + && allowAppConfigs) { + String pkgname = parser.getAttributeValue(null, "package"); + String carrierPkgname = parser.getAttributeValue(null, "carrierAppPackage"); + if (pkgname == null || carrierPkgname == null) { + Slog.w(TAG, " associatedPkgs = + mDisabledUntilUsedPreinstalledCarrierAssociatedApps.get( + carrierPkgname); + if (associatedPkgs == null) { + associatedPkgs = new ArrayList<>(); + mDisabledUntilUsedPreinstalledCarrierAssociatedApps.put( + carrierPkgname, associatedPkgs); + } + associatedPkgs.add(pkgname); + } + XmlUtils.skipCurrentTag(parser); } else { XmlUtils.skipCurrentTag(parser); continue; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index b907be07fab9..c1d47840abf6 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1963,10 +1963,6 @@ public class PackageManagerService extends IPackageManager.Stub { PackageManagerService m = new PackageManagerService(context, installer, factoryTest, onlyCore); m.enableSystemUserPackages(); - // Disable any carrier apps. We do this very early in boot to prevent the apps from being - // disabled after already being started. - CarrierAppUtils.disableCarrierAppsUntilPrivileged(context.getOpPackageName(), m, - UserHandle.USER_SYSTEM); ServiceManager.addService("package", m); return m; } @@ -17872,6 +17868,11 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); public void systemReady() { mSystemReady = true; + // Disable any carrier apps. We do this very early in boot to prevent the apps from being + // disabled after already being started. + CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), this, + mContext.getContentResolver(), UserHandle.USER_SYSTEM); + // Read the compatibilty setting when the system is ready. boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt( mContext.getContentResolver(), diff --git a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java index ca7354f66a1b..8b81b0d82b71 100644 --- a/telephony/java/com/android/internal/telephony/CarrierAppUtils.java +++ b/telephony/java/com/android/internal/telephony/CarrierAppUtils.java @@ -17,18 +17,23 @@ package com.android.internal.telephony; import android.annotation.Nullable; +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; +import android.util.ArrayMap; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.SystemConfig; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * Utilities for handling carrier applications. @@ -53,6 +58,11 @@ public final class CarrierAppUtils { * in the default state (e.g. not explicitly DISABLED/DISABLED_BY_USER/ENABLED), or we enable if * the app is carrier privileged and in either the default state or DISABLED_UNTIL_USED. * + * In addition, there is a list of carrier-associated applications in + * {@link SystemConfig#getDisabledUntilUsedPreinstalledCarrierAssociatedApps}. Each app in this + * list is associated with a carrier app. When the given carrier app is enabled/disabled per the + * above, the associated applications are enabled/disabled to match. + * * When enabling a carrier app we also grant it default permissions. * * This method is idempotent and is safe to be called at any time; it should be called once at @@ -60,19 +70,24 @@ public final class CarrierAppUtils { * privileged apps may have changed. */ public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage, - IPackageManager packageManager, TelephonyManager telephonyManager, int userId) { + IPackageManager packageManager, TelephonyManager telephonyManager, + ContentResolver contentResolver, int userId) { if (DEBUG) { Slog.d(TAG, "disableCarrierAppsUntilPrivileged"); } + SystemConfig config = SystemConfig.getInstance(); String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray( com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps); - disableCarrierAppsUntilPrivileged(callingPackage, packageManager, telephonyManager, userId, - systemCarrierAppsDisabledUntilUsed); + ArrayMap> systemCarrierAssociatedAppsDisabledUntilUsed = + config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps(); + disableCarrierAppsUntilPrivileged(callingPackage, packageManager, telephonyManager, + contentResolver, userId, systemCarrierAppsDisabledUntilUsed, + systemCarrierAssociatedAppsDisabledUntilUsed); } /** * Like {@link #disableCarrierAppsUntilPrivileged(String, IPackageManager, TelephonyManager, - * int)}, but assumes that no carrier apps have carrier privileges. + * ContentResolver, int)}, but assumes that no carrier apps have carrier privileges. * * This prevents a potential race condition on first boot - since the app's default state is * enabled, we will initially disable it when the telephony stack is first initialized as it has @@ -82,29 +97,43 @@ public final class CarrierAppUtils { * Manager can kill it, and this can lead to crashes as the app is in an unexpected state. */ public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage, - IPackageManager packageManager, int userId) { + IPackageManager packageManager, ContentResolver contentResolver, int userId) { if (DEBUG) { Slog.d(TAG, "disableCarrierAppsUntilPrivileged"); } + SystemConfig config = SystemConfig.getInstance(); String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray( com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps); + ArrayMap> systemCarrierAssociatedAppsDisabledUntilUsed = + config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps(); disableCarrierAppsUntilPrivileged(callingPackage, packageManager, - null /* telephonyManager */, userId, systemCarrierAppsDisabledUntilUsed); + null /* telephonyManager */, contentResolver, userId, + systemCarrierAppsDisabledUntilUsed, systemCarrierAssociatedAppsDisabledUntilUsed); } // Must be public b/c framework unit tests can't access package-private methods. @VisibleForTesting public static void disableCarrierAppsUntilPrivileged(String callingPackage, - IPackageManager packageManager, @Nullable TelephonyManager telephonyManager, int userId, - String[] systemCarrierAppsDisabledUntilUsed) { + IPackageManager packageManager, @Nullable TelephonyManager telephonyManager, + ContentResolver contentResolver, int userId, + String[] systemCarrierAppsDisabledUntilUsed, + ArrayMap> systemCarrierAssociatedAppsDisabledUntilUsed) { List candidates = getDefaultCarrierAppCandidatesHelper(packageManager, userId, systemCarrierAppsDisabledUntilUsed); if (candidates == null || candidates.isEmpty()) { return; } + Map> associatedApps = getDefaultCarrierAssociatedAppsHelper( + packageManager, + userId, + systemCarrierAssociatedAppsDisabledUntilUsed); + List enabledCarrierPackages = new ArrayList<>(); + boolean hasRunOnce = Settings.Secure.getIntForUser( + contentResolver, Settings.Secure.CARRIER_APPS_HANDLED, 0, userId) == 1; + try { for (ApplicationInfo ai : candidates) { String packageName = ai.packageName; @@ -112,35 +141,94 @@ public final class CarrierAppUtils { telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; - // Only update enabled state for the app on /system. Once it has been updated we - // shouldn't touch it. - if (!ai.isUpdatedSystemApp()) { - if (hasPrivileges - && (ai.enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT + if (hasPrivileges) { + // Only update enabled state for the app on /system. Once it has been + // updated we shouldn't touch it. + if (!ai.isUpdatedSystemApp() + && (ai.enabledSetting == + PackageManager.COMPONENT_ENABLED_STATE_DEFAULT || ai.enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) { Slog.i(TAG, "Update state(" + packageName + "): ENABLED for user " + userId); - packageManager.setApplicationEnabledSetting(packageName, + packageManager.setApplicationEnabledSetting( + packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, - PackageManager.DONT_KILL_APP, userId, callingPackage); - } else if (!hasPrivileges + PackageManager.DONT_KILL_APP, + userId, + callingPackage); + } + + // Also enable any associated apps for this carrier app. + List 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) { + Slog.i(TAG, "Update associated state(" + associatedApp.packageName + + "): ENABLED for user " + userId); + packageManager.setApplicationEnabledSetting( + associatedApp.packageName, + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, + PackageManager.DONT_KILL_APP, + userId, + callingPackage); + } + } + } + + // Always re-grant default permissions to carrier apps w/ privileges. + enabledCarrierPackages.add(ai.packageName); + } else { // No carrier privileges + // Only update enabled state for the app on /system. Once it has been + // updated we shouldn't touch it. + if (!ai.isUpdatedSystemApp() && ai.enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { Slog.i(TAG, "Update state(" + packageName + "): DISABLED_UNTIL_USED for user " + userId); - packageManager.setApplicationEnabledSetting(packageName, - PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0, - userId, callingPackage); + packageManager.setApplicationEnabledSetting( + packageName, + PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, + 0, + userId, + callingPackage); } - } - // Always re-grant default permissions to carrier apps w/ privileges. - if (hasPrivileges) { - enabledCarrierPackages.add(ai.packageName); + // 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 associatedAppList = associatedApps.get(packageName); + if (associatedAppList != null) { + for (ApplicationInfo associatedApp : associatedAppList) { + if (associatedApp.enabledSetting + == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { + Slog.i(TAG, + "Update associated state(" + associatedApp.packageName + + "): DISABLED_UNTIL_USED for user " + userId); + packageManager.setApplicationEnabledSetting( + associatedApp.packageName, + PackageManager + .COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, + 0, + userId, + callingPackage); + } + } + } + } } } + // Mark the execution so we do not disable apps again. + if (!hasRunOnce) { + Settings.Secure.putIntForUser( + contentResolver, Settings.Secure.CARRIER_APPS_HANDLED, 1, userId); + } + if (!enabledCarrierPackages.isEmpty()) { // Since we enabled at least one app, ensure we grant default permissions to those // apps. @@ -190,8 +278,8 @@ public final class CarrierAppUtils { * * These are the apps subject to the hiding/showing logic in * {@link CarrierAppUtils#disableCarrierAppsUntilPrivileged(String, IPackageManager, - * TelephonyManager, int)}, as well as the apps which should have default permissions granted, - * when a matching SIM is inserted. + * TelephonyManager, ContentResolver, int)}, as well as the apps which should have default + * permissions granted, when a matching SIM is inserted. * * Whether or not the app is actually considered a default app depends on whether the app has * carrier privileges as determined by the SIMs in the device. @@ -205,30 +293,68 @@ public final class CarrierAppUtils { } private static List getDefaultCarrierAppCandidatesHelper( - IPackageManager packageManager, int userId, + IPackageManager packageManager, + int userId, String[] systemCarrierAppsDisabledUntilUsed) { if (systemCarrierAppsDisabledUntilUsed == null || systemCarrierAppsDisabledUntilUsed.length == 0) { return null; } - List apps = null; - try { - apps = new ArrayList<>(systemCarrierAppsDisabledUntilUsed.length); - for (String packageName : systemCarrierAppsDisabledUntilUsed) { - ApplicationInfo ai = packageManager.getApplicationInfo(packageName, - PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS, userId); - if (ai == null) { - // No app found for packageName - continue; - } - if (!ai.isSystemApp()) { - continue; - } + List apps = new ArrayList<>(systemCarrierAppsDisabledUntilUsed.length); + for (int i = 0; i < systemCarrierAppsDisabledUntilUsed.length; i++) { + String packageName = systemCarrierAppsDisabledUntilUsed[i]; + ApplicationInfo ai = + getApplicationInfoIfSystemApp(packageManager, userId, packageName); + if (ai != null) { apps.add(ai); } + } + return apps; + } + + private static Map> getDefaultCarrierAssociatedAppsHelper( + IPackageManager packageManager, + int userId, + ArrayMap> systemCarrierAssociatedAppsDisabledUntilUsed) { + int size = systemCarrierAssociatedAppsDisabledUntilUsed.size(); + Map> associatedApps = new ArrayMap<>(size); + for (int i = 0; i < size; i++) { + String carrierAppPackage = systemCarrierAssociatedAppsDisabledUntilUsed.keyAt(i); + List associatedAppPackages = + systemCarrierAssociatedAppsDisabledUntilUsed.valueAt(i); + for (int j = 0; j < associatedAppPackages.size(); j++) { + ApplicationInfo ai = + getApplicationInfoIfSystemApp( + packageManager, userId, associatedAppPackages.get(j)); + // Only update enabled state for the app on /system. Once it has been updated we + // shouldn't touch it. + if (ai != null && !ai.isUpdatedSystemApp()) { + List appList = associatedApps.get(carrierAppPackage); + if (appList == null) { + appList = new ArrayList<>(); + associatedApps.put(carrierAppPackage, appList); + } + appList.add(ai); + } + } + } + return associatedApps; + } + + @Nullable + private static ApplicationInfo getApplicationInfoIfSystemApp( + IPackageManager packageManager, + int userId, + String packageName) { + try { + ApplicationInfo ai = packageManager.getApplicationInfo(packageName, + PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS, userId); + if (ai != null && ai.isSystemApp()) { + return ai; + } } catch (RemoteException e) { Slog.w(TAG, "Could not reach PackageManager", e); } - return apps; + return null; } } -- cgit v1.2.3-59-g8ed1b