From 1172ffa7c261a499132fb59e18c2f972b1f57f45 Mon Sep 17 00:00:00 2001 From: Hunter Knepshield Date: Mon, 15 Jun 2020 19:42:15 -0700 Subject: [PATCH 1/2] Add new "addedInSdk" attribute to carrier-associated apps. Previously, the sysconfig wasn't capable of understanding carrier-associated apps that were added after a device's initial launch (i.e. via OTA) because the logic in CarrierAppUtils explicitly avoids disabling such apps a second time. Most of this change is just plumbing everything through. For now, it's all @hide due to R API deadlines. It will be made public in S. Bug: 154872019 Test: manual, QA, atest FrameworksTelephonyTests:CarrierAppUtilsTest Change-Id: I530a4f73146b09879547ca2e0c26428957fef37a --- .../android/os/CarrierAssociatedAppEntry.aidl | 19 ++++++ .../android/os/CarrierAssociatedAppEntry.java | 68 +++++++++++++++++++ core/java/android/os/ISystemConfig.aidl | 5 ++ core/java/android/os/SystemConfigManager.java | 25 +++++++ .../java/com/android/server/SystemConfig.java | 29 ++++++-- .../android/server/SystemConfigService.java | 16 +++++ 6 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 core/java/android/os/CarrierAssociatedAppEntry.aidl create mode 100644 core/java/android/os/CarrierAssociatedAppEntry.java diff --git a/core/java/android/os/CarrierAssociatedAppEntry.aidl b/core/java/android/os/CarrierAssociatedAppEntry.aidl new file mode 100644 index 0000000000000..a9b1055ee1744 --- /dev/null +++ b/core/java/android/os/CarrierAssociatedAppEntry.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 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. + */ + +package android.os; + +parcelable CarrierAssociatedAppEntry; diff --git a/core/java/android/os/CarrierAssociatedAppEntry.java b/core/java/android/os/CarrierAssociatedAppEntry.java new file mode 100644 index 0000000000000..13f6eb63e29c8 --- /dev/null +++ b/core/java/android/os/CarrierAssociatedAppEntry.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2020 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. + */ +package android.os; + +/** + * Represents a carrier app entry for use with {@link SystemConfigService}. + * + * @hide + */ +public final class CarrierAssociatedAppEntry implements Parcelable { + + /** + * For carrier-associated app entries that don't specify the addedInSdk XML + * attribute. + */ + public static final int SDK_UNSPECIFIED = -1; + + public final String packageName; + /** May be {@link #SDK_UNSPECIFIED}. */ + public final int addedInSdk; + + public CarrierAssociatedAppEntry(String packageName, int addedInSdk) { + this.packageName = packageName; + this.addedInSdk = addedInSdk; + } + + public CarrierAssociatedAppEntry(Parcel in) { + packageName = in.readString(); + addedInSdk = in.readInt(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(packageName); + dest.writeInt(addedInSdk); + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public CarrierAssociatedAppEntry createFromParcel(Parcel source) { + return new CarrierAssociatedAppEntry(source); + } + + @Override + public CarrierAssociatedAppEntry[] newArray(int size) { + return new CarrierAssociatedAppEntry[size]; + } + }; +} diff --git a/core/java/android/os/ISystemConfig.aidl b/core/java/android/os/ISystemConfig.aidl index d3b029854112f..52f0ce1f054f3 100644 --- a/core/java/android/os/ISystemConfig.aidl +++ b/core/java/android/os/ISystemConfig.aidl @@ -30,4 +30,9 @@ interface ISystemConfig { * @see SystemConfigManager#getDisabledUntilUsedPreinstalledCarrierAssociatedApps */ Map getDisabledUntilUsedPreinstalledCarrierAssociatedApps(); + + /** + * @see SystemConfigManager#getDisabledUntilUsedPreinstalledCarrierAssociatedAppEntries + */ + Map getDisabledUntilUsedPreinstalledCarrierAssociatedAppEntries(); } diff --git a/core/java/android/os/SystemConfigManager.java b/core/java/android/os/SystemConfigManager.java index 3a9ce2fa85f13..12a1ffaf69c15 100644 --- a/core/java/android/os/SystemConfigManager.java +++ b/core/java/android/os/SystemConfigManager.java @@ -88,4 +88,29 @@ public class SystemConfigManager { return Collections.emptyMap(); } } + + /** + * Returns a map that describes helper apps associated with carrier apps that, like the apps + * returned by {@link #getDisabledUntilUsedPreinstalledCarrierApps()}, should be disabled until + * the correct SIM is inserted into the device. + * + *

TODO(b/159069037) expose this and get rid of the other method that omits SDK version. + * + * @return A map with keys corresponding to package names returned by + * {@link #getDisabledUntilUsedPreinstalledCarrierApps()} and values as lists of package + * names of helper apps and the SDK versions when they were first added. + * + * @hide + */ + @RequiresPermission(Manifest.permission.READ_CARRIER_APP_INFO) + public @NonNull Map> + getDisabledUntilUsedPreinstalledCarrierAssociatedAppEntries() { + try { + return (Map>) + mInterface.getDisabledUntilUsedPreinstalledCarrierAssociatedAppEntries(); + } catch (RemoteException e) { + Log.e(TAG, "Caught remote exception", e); + return Collections.emptyMap(); + } + } } diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index d9ca9c2f87f55..ea390cd71e318 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -24,6 +24,7 @@ import android.content.ComponentName; import android.content.pm.FeatureInfo; import android.content.pm.PackageManager; import android.os.Build; +import android.os.CarrierAssociatedAppEntry; import android.os.Environment; import android.os.FileUtils; import android.os.Process; @@ -198,8 +199,8 @@ public class SystemConfig { // 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<>(); + final ArrayMap> + mDisabledUntilUsedPreinstalledCarrierAssociatedApps = new ArrayMap<>(); final ArrayMap> mPrivAppPermissions = new ArrayMap<>(); final ArrayMap> mPrivAppDenyPermissions = new ArrayMap<>(); @@ -331,7 +332,8 @@ public class SystemConfig { return mDisabledUntilUsedPreinstalledCarrierApps; } - public ArrayMap> getDisabledUntilUsedPreinstalledCarrierAssociatedApps() { + public ArrayMap> + getDisabledUntilUsedPreinstalledCarrierAssociatedApps() { return mDisabledUntilUsedPreinstalledCarrierAssociatedApps; } @@ -954,7 +956,23 @@ public class SystemConfig { + "> without package or carrierAppPackage in " + permFile + " at " + parser.getPositionDescription()); } else { - List associatedPkgs = + // APKs added to system images via OTA should specify the addedInSdk + // attribute, otherwise they may be enabled-by-default in too many + // cases. See CarrierAppUtils for more info. + int addedInSdk = CarrierAssociatedAppEntry.SDK_UNSPECIFIED; + String addedInSdkStr = parser.getAttributeValue(null, "addedInSdk"); + if (!TextUtils.isEmpty(addedInSdkStr)) { + try { + addedInSdk = Integer.parseInt(addedInSdkStr); + } catch (NumberFormatException e) { + Slog.w(TAG, "<" + name + "> addedInSdk not an integer in " + + permFile + " at " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + break; + } + } + List associatedPkgs = mDisabledUntilUsedPreinstalledCarrierAssociatedApps.get( carrierPkgname); if (associatedPkgs == null) { @@ -962,7 +980,8 @@ public class SystemConfig { mDisabledUntilUsedPreinstalledCarrierAssociatedApps.put( carrierPkgname, associatedPkgs); } - associatedPkgs.add(pkgname); + associatedPkgs.add( + new CarrierAssociatedAppEntry(pkgname, addedInSdk)); } } else { logNotAllowedInPartition(name, permFile, parser); diff --git a/services/java/com/android/server/SystemConfigService.java b/services/java/com/android/server/SystemConfigService.java index e8ab10124ef8b..1801f3bca30ea 100644 --- a/services/java/com/android/server/SystemConfigService.java +++ b/services/java/com/android/server/SystemConfigService.java @@ -15,6 +15,9 @@ */ package com.android.server; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toMap; + import android.Manifest; import android.content.Context; import android.os.ISystemConfig; @@ -45,6 +48,19 @@ public class SystemConfigService extends SystemService { mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_CARRIER_APP_INFO, "getDisabledUntilUsedPreInstalledCarrierAssociatedApps requires" + " READ_CARRIER_APP_INFO"); + return SystemConfig.getInstance() + .getDisabledUntilUsedPreinstalledCarrierAssociatedApps().entrySet().stream() + .collect(toMap( + Map.Entry::getKey, + e -> e.getValue().stream().map(app -> app.packageName) + .collect(toList()))); + } + + @Override + public Map getDisabledUntilUsedPreinstalledCarrierAssociatedAppEntries() { + mContext.enforceCallingOrSelfPermission(Manifest.permission.READ_CARRIER_APP_INFO, + "getDisabledUntilUsedPreInstalledCarrierAssociatedAppEntries requires" + + " READ_CARRIER_APP_INFO"); return SystemConfig.getInstance() .getDisabledUntilUsedPreinstalledCarrierAssociatedApps(); } From 87cd4b076176539d5e3520e4a10dfaa380380c52 Mon Sep 17 00:00:00 2001 From: Hunter Knepshield Date: Fri, 12 Jun 2020 15:02:39 -0700 Subject: [PATCH 2/2] Reevaluate some carrier-associated apps' status on SDK changes. If a carrier-associated app is added to the system image after the device is initially launched (e.g. Q -> R OTA), the logic to disable apps without a corresponding SIM will not run, leaving the app in an enabled state in more cases than there should be. To safely account for this, we only reevaluate on SDK change for apps whose addedInSdk value falls within the range since the last time we evaluated carrier apps' statuses. Apps that set lower (or higher) SDK versions are ignored. We choose *not* to use minimum or target SDK version here because it's more likely to change with each OTA / prebuilt, which could cause unintended disable operations (or lack thereof). Bug: 154872019 Test: manual, QA, atest FrameworksTelephonyTests:CarrierAppUtilsTest Change-Id: I97ddfa03f34e7d00e66ce7cd508d6d2a5e83fbc2 --- core/java/android/provider/Settings.java | 7 +- .../internal/telephony/CarrierAppUtils.java | 158 +++++++++++------- 2 files changed, 106 insertions(+), 59 deletions(-) diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4f0a9728fcf8d..2193e5a47da9a 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8517,14 +8517,15 @@ public final class Settings { public static final int VR_DISPLAY_MODE_OFF = 1; /** - * Whether CarrierAppUtils#disableCarrierAppsUntilPrivileged has been executed at least - * once. + * The latest SDK version that CarrierAppUtils#disableCarrierAppsUntilPrivileged has been + * executed for. * *

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. + *

Will be set to {@link android.os.Build.VERSION#SDK_INT} once executed. Note that older + * SDK versions prior to R set 1 for this value. * * @hide */ diff --git a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java index 4606fb4b631c2..e57b030987586 100644 --- a/telephony/common/com/android/internal/telephony/CarrierAppUtils.java +++ b/telephony/common/com/android/internal/telephony/CarrierAppUtils.java @@ -21,6 +21,8 @@ import android.content.ContentResolver; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.os.Build; +import android.os.CarrierAssociatedAppEntry; import android.os.SystemConfigManager; import android.os.UserHandle; import android.permission.PermissionManager; @@ -79,8 +81,8 @@ public final class CarrierAppUtils { SystemConfigManager config = context.getSystemService(SystemConfigManager.class); Set systemCarrierAppsDisabledUntilUsed = config.getDisabledUntilUsedPreinstalledCarrierApps(); - Map> systemCarrierAssociatedAppsDisabledUntilUsed = - config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps(); + Map> systemCarrierAssociatedAppsDisabledUntilUsed = + config.getDisabledUntilUsedPreinstalledCarrierAssociatedAppEntries(); ContentResolver contentResolver = getContentResolverForUser(context, userId); disableCarrierAppsUntilPrivileged(callingPackage, telephonyManager, contentResolver, userId, systemCarrierAppsDisabledUntilUsed, @@ -107,8 +109,8 @@ public final class CarrierAppUtils { Set systemCarrierAppsDisabledUntilUsed = config.getDisabledUntilUsedPreinstalledCarrierApps(); - Map> systemCarrierAssociatedAppsDisabledUntilUsed = - config.getDisabledUntilUsedPreinstalledCarrierAssociatedApps(); + Map> systemCarrierAssociatedAppsDisabledUntilUsed = + config.getDisabledUntilUsedPreinstalledCarrierAssociatedAppEntries(); ContentResolver contentResolver = getContentResolverForUser(context, userId); disableCarrierAppsUntilPrivileged(callingPackage, null /* telephonyManager */, contentResolver, userId, systemCarrierAppsDisabledUntilUsed, @@ -138,8 +140,8 @@ public final class CarrierAppUtils { public static void disableCarrierAppsUntilPrivileged(String callingPackage, @Nullable TelephonyManager telephonyManager, ContentResolver contentResolver, int userId, Set systemCarrierAppsDisabledUntilUsed, - Map> systemCarrierAssociatedAppsDisabledUntilUsed, - Context context) { + Map> + systemCarrierAssociatedAppsDisabledUntilUsed, Context context) { PackageManager packageManager = context.getPackageManager(); PermissionManager permissionManager = (PermissionManager) context.getSystemService(Context.PERMISSION_SERVICE); @@ -149,12 +151,17 @@ public final class CarrierAppUtils { return; } - Map> associatedApps = getDefaultCarrierAssociatedAppsHelper( + Map> associatedApps = getDefaultCarrierAssociatedAppsHelper( userId, systemCarrierAssociatedAppsDisabledUntilUsed, context); List enabledCarrierPackages = new ArrayList<>(); - boolean hasRunOnce = Settings.Secure.getInt(contentResolver, - Settings.Secure.CARRIER_APPS_HANDLED, 0) == 1; + int carrierAppsHandledSdk = + Settings.Secure.getInt(contentResolver, Settings.Secure.CARRIER_APPS_HANDLED, 0); + if (DEBUG) { + Log.i(TAG, "Last execution SDK: " + carrierAppsHandledSdk); + } + boolean hasRunEver = carrierAppsHandledSdk != 0; // SDKs < R used to just set 1 here + boolean hasRunForSdk = carrierAppsHandledSdk == Build.VERSION.SDK_INT; try { for (ApplicationInfo ai : candidates) { @@ -166,10 +173,10 @@ public final class CarrierAppUtils { // add hiddenUntilInstalled flag for carrier apps and associated apps packageManager.setSystemAppState( packageName, PackageManager.SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN); - List associatedAppList = associatedApps.get(packageName); + List associatedAppList = associatedApps.get(packageName); if (associatedAppList != null) { - for (ApplicationInfo associatedApp : associatedAppList) { - packageManager.setSystemAppState(associatedApp.packageName, + for (AssociatedAppInfo associatedApp : associatedAppList) { + packageManager.setSystemAppState(associatedApp.appInfo.packageName, PackageManager.SYSTEM_APP_STATE_HIDDEN_UNTIL_INSTALLED_HIDDEN); } } @@ -184,7 +191,7 @@ public final class CarrierAppUtils { || enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED || (ai.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { - Log.i(TAG, "Update state(" + packageName + "): ENABLED for user " + Log.i(TAG, "Update state (" + packageName + "): ENABLED for user " + userId); context.createContextAsUser(UserHandle.of(userId), 0) .getPackageManager() @@ -200,28 +207,37 @@ public final class CarrierAppUtils { // Also enable any associated apps for this carrier app. if (associatedAppList != null) { - for (ApplicationInfo associatedApp : associatedAppList) { + for (AssociatedAppInfo associatedApp : associatedAppList) { int associatedAppEnabledSetting = context .createContextAsUser(UserHandle.of(userId), 0) .getPackageManager() - .getApplicationEnabledSetting(associatedApp.packageName); + .getApplicationEnabledSetting( + associatedApp.appInfo.packageName); + boolean associatedAppInstalled = (associatedApp.appInfo.flags + & ApplicationInfo.FLAG_INSTALLED) != 0; + if (DEBUG) { + Log.i(TAG, "(hasPrivileges) associated app " + + associatedApp.appInfo.packageName + ", enabled = " + + associatedAppEnabledSetting + ", installed = " + + associatedAppInstalled); + } if (associatedAppEnabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT || associatedAppEnabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED - || (associatedApp.flags - & ApplicationInfo.FLAG_INSTALLED) == 0) { - Log.i(TAG, "Update associated state(" + associatedApp.packageName - + "): ENABLED for user " + userId); + || !associatedAppInstalled) { + Log.i(TAG, "Update associated state (" + + associatedApp.appInfo.packageName + "): ENABLED for user " + + userId); context.createContextAsUser(UserHandle.of(userId), 0) .getPackageManager() - .setSystemAppState(associatedApp.packageName, + .setSystemAppState(associatedApp.appInfo.packageName, PackageManager.SYSTEM_APP_STATE_INSTALLED); context.createPackageContextAsUser( callingPackage, 0, UserHandle.of(userId)) .getPackageManager() .setApplicationEnabledSetting( - associatedApp.packageName, + associatedApp.appInfo.packageName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); } @@ -236,7 +252,7 @@ public final class CarrierAppUtils { if (!isUpdatedSystemApp(ai) && enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT && (ai.flags & ApplicationInfo.FLAG_INSTALLED) != 0) { - Log.i(TAG, "Update state(" + packageName + Log.i(TAG, "Update state (" + packageName + "): DISABLED_UNTIL_USED for user " + userId); context.createContextAsUser(UserHandle.of(userId), 0) .getPackageManager() @@ -244,37 +260,56 @@ public final class CarrierAppUtils { packageName, PackageManager.SYSTEM_APP_STATE_UNINSTALLED); } - // 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) { - if (associatedAppList != null) { - for (ApplicationInfo associatedApp : associatedAppList) { - int associatedAppEnabledSetting = context - .createContextAsUser(UserHandle.of(userId), 0) + // Associated apps are more brittle, because we can't rely on the distinction + // between "default" and "enabled". To account for this, we have two cases: + // 1. We've never run before, so we're fine to disable all associated apps. + // 2. We've run before, but not on this SDK version, so we will only operate on + // apps with addedInSdk in the range (lastHandledSdk, currentSdk]. + // Otherwise, don't touch the associated apps. + if (associatedAppList != null) { + for (AssociatedAppInfo associatedApp : associatedAppList) { + boolean allowDisable = !hasRunEver || (!hasRunForSdk + && associatedApp.addedInSdk + != CarrierAssociatedAppEntry.SDK_UNSPECIFIED + && associatedApp.addedInSdk > carrierAppsHandledSdk + && associatedApp.addedInSdk <= Build.VERSION.SDK_INT); + int associatedAppEnabledSetting = context + .createContextAsUser(UserHandle.of(userId), 0) + .getPackageManager() + .getApplicationEnabledSetting( + associatedApp.appInfo.packageName); + boolean associatedAppInstalled = (associatedApp.appInfo.flags + & ApplicationInfo.FLAG_INSTALLED) != 0; + if (DEBUG) { + Log.i(TAG, "(!hasPrivileges) associated app " + + associatedApp.appInfo.packageName + ", allowDisable = " + + allowDisable + ", addedInSdk = " + + associatedApp.addedInSdk + ", enabled = " + + associatedAppEnabledSetting + ", installed = " + + associatedAppInstalled); + } + if (allowDisable + && associatedAppEnabledSetting + == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT + && associatedAppInstalled) { + Log.i(TAG, + "Update associated state (" + + associatedApp.appInfo.packageName + + "): DISABLED_UNTIL_USED for user " + userId); + context.createContextAsUser(UserHandle.of(userId), 0) .getPackageManager() - .getApplicationEnabledSetting(associatedApp.packageName); - if (associatedAppEnabledSetting - == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT - && (associatedApp.flags - & ApplicationInfo.FLAG_INSTALLED) != 0) { - Log.i(TAG, - "Update associated state(" + associatedApp.packageName - + "): DISABLED_UNTIL_USED for user " + userId); - context.createContextAsUser(UserHandle.of(userId), 0) - .getPackageManager() - .setSystemAppState(associatedApp.packageName, - PackageManager.SYSTEM_APP_STATE_UNINSTALLED); - } + .setSystemAppState(associatedApp.appInfo.packageName, + PackageManager.SYSTEM_APP_STATE_UNINSTALLED); } } } } } - // Mark the execution so we do not disable apps again. - if (!hasRunOnce) { - Settings.Secure.putInt(contentResolver, Settings.Secure.CARRIER_APPS_HANDLED, 1); + // Mark the execution so we do not disable apps again on this SDK version. + if (!hasRunEver || !hasRunForSdk) { + Settings.Secure.putInt(contentResolver, Settings.Secure.CARRIER_APPS_HANDLED, + Build.VERSION.SDK_INT); } if (!enabledCarrierPackages.isEmpty()) { @@ -360,28 +395,28 @@ public final class CarrierAppUtils { return apps; } - private static Map> getDefaultCarrierAssociatedAppsHelper( - int userId, Map> systemCarrierAssociatedAppsDisabledUntilUsed, - Context context) { + private static Map> getDefaultCarrierAssociatedAppsHelper( + int userId, Map> + systemCarrierAssociatedAppsDisabledUntilUsed, Context context) { int size = systemCarrierAssociatedAppsDisabledUntilUsed.size(); - Map> associatedApps = new ArrayMap<>(size); - for (Map.Entry> entry + Map> associatedApps = new ArrayMap<>(size); + for (Map.Entry> entry : systemCarrierAssociatedAppsDisabledUntilUsed.entrySet()) { String carrierAppPackage = entry.getKey(); - List associatedAppPackages = entry.getValue(); + List associatedAppPackages = entry.getValue(); for (int j = 0; j < associatedAppPackages.size(); j++) { + CarrierAssociatedAppEntry associatedApp = associatedAppPackages.get(j); ApplicationInfo ai = - getApplicationInfoIfSystemApp( - userId, associatedAppPackages.get(j), context); + getApplicationInfoIfSystemApp(userId, associatedApp.packageName, context); // Only update enabled state for the app on /system. Once it has been updated we // shouldn't touch it. if (ai != null && !isUpdatedSystemApp(ai)) { - List appList = associatedApps.get(carrierAppPackage); + List appList = associatedApps.get(carrierAppPackage); if (appList == null) { appList = new ArrayList<>(); associatedApps.put(carrierAppPackage, appList); } - appList.add(ai); + appList.add(new AssociatedAppInfo(ai, associatedApp.addedInSdk)); } } } @@ -406,4 +441,15 @@ public final class CarrierAppUtils { } return null; } + + private static final class AssociatedAppInfo { + public final ApplicationInfo appInfo; + // Might be CarrierAssociatedAppEntry.SDK_UNSPECIFIED. + public final int addedInSdk; + + AssociatedAppInfo(ApplicationInfo appInfo, int addedInSdk) { + this.appInfo = appInfo; + this.addedInSdk = addedInSdk; + } + } }